/*
** Starscream 680x0 emulation library
** Copyright 1997, 1998, 1999 Neill Corlett
**
** Refer to STARDOC.TXT for terms of use, API reference, and directions on
** how to compile.
*/

#define VERSION "0.27"

/***************************************************************************/
/*
** NOTE
**
** All 68020-related variables and functions are currently experimental, and
** unsupported.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>

/***************************************************************************/
/*
** Register Usage
** --------------
**
** This is fairly consistent throughout the file.  Occasionally, EAX or EDX
** will be used as a scratch register (in some cases the x86 instruction set
** demands this).
**
** EAX: Bit   0  : V flag
**      Bit  1-7 : MUST BE ZERO
**      Bit   8  : C flag
**      Bit  14  : Z flag
**      Bit  15  : N flag
**      Bit 16-31: undefined
** EBX: Lower 16 bits: Current instruction word or register number
**      Upper 16 bits: zero
** ECX: Primary data
** EDX: Primary address
** EBP: State pointer
** ESI: Current PC, including base offset
** EDI: Cycle counter
*/
/***************************************************************************/
/*
**  68010 Loop Mode Timing
**  ----------------------
**
**  Loop mode is implemented entirely in the DBRA instruction.  It will
**  detect when it's supposed to be in loop mode, and adjust its timing when
**  applicable.
**
**  The __loopmode variable controls when loop mode is active.  It is set to
**  1 after an eligible loop is completed.  It is set to 0 when the loop
**  terminates, or when an interrupt / exception occurs.
**
**  Loop info byte:
**
**  Bits 1-3:  Continuation cycles / 2
**  Bits 4-6:  Termination cycles / 2
**  Bits 7-0:  (Expiration cycles - continuation cycles) / 2
**  (bit 7 wraps around to bit 0)
**
**  With the loop info byte in AL:
**     To get the continuation cycles:
**          and eax,byte 0Eh
**     To get the termination cycles:
**          shr al,3
**          and eax,byte 0Eh
**     To get the continue/expire difference:
**          rol al,2
**          and eax,byte 06h
**
**  Default = DBh
**           (11011011)
**                101 : 101 = 5   2*5            = 10 continuation cycles
**             101    : 101 = 5   2*5            = 10 termination cycles
**            1      1:  11 = 3   2*3 = 6   10+6 = 16 expiration cycles
**
**  (10/10/16 corresponds to the normal DBRA timing behavior)
*/
/***************************************************************************/
/*
** Algorithm for trace checkpoint in s680x0exec:
**
** If the SR trace flag is set {
**   Set the trace trickybit.  This differentiates us from the out-of-time
**    case above.
**   Set cycles_leftover = cycles_needed.
**   Force a context switch.
** } otherwise {
**   Clear the trace trickybit.
** }
** Begin the fetch/decode/execute loop as usual.
**
**
** In selected ret_timing routines:
**
** Subtract usual number of cycles from edi
** If I'm out of time (edi is negative) {
**   Jump to execend with SF set, as usual.
** } otherwise (guaranteed at least one more instruction) {
**   Jump to the s680x0exec trace checkpoint.
** }
**
**
** Make sure that the group 1 exception handler clears the trace trickybit.
**
**
** Upon reaching execend:
**
** If the trace trickybit is set {
**   Set cycles_needed = cycles_leftover.
**   Add cycles_needed to edi.
**   Generate a trace exception (clearing the SR trace flag in the process).
**   Clear the trace trickybit.
**   If edi is positive, resume the fetch/decode/execute loop.
**    Otherwise, fall through to the usual epilogue code.
** }
*/
/***************************************************************************/
/*
** Rebasing notes
** --------------
**
** Cached rebase happens on:
**  * JMP, JSR, RTS, RTE, RTR, RTD
**  * Exceptions (except hardware interrupts)
**
** Uncached rebase happens on:
**  * Entry to s680x0exec()
**  * Hardware interrupts
**  * Supervisor flag change
**  * Cache disable/invalidate (68020+)
**
** I can't think of any good reason why the hardware interrupt case should be
** uncached, except it happens to be convenient.
*/

typedef unsigned char  byte;
typedef unsigned short word;
typedef unsigned int   dword;

static int use_stack   = -1;
static int hog         = -1;
static int addressbits = -1;
static int cputype     = -1;
static char *sourcename = NULL;

/* This counts the number of instruction handling routines.  There's not much
** point to it except for curiosity. */
static int routine_counter = 0;

/* Misc. constants */
static char *x86ax   [5] = {"?", "al"  , "ax"  , "?", "eax"  };
static char *x86bx   [5] = {"?", "bl"  , "bx"  , "?", "ebx"  };
static char *x86cx   [5] = {"?", "cl"  , "cx"  , "?", "ecx"  };
static char *x86dx   [5] = {"?", "dl"  , "dx"  , "?", "edx"  };
static char *sizename[5] = {"?", "byte", "word", "?", "dword"};
static int quickvalue[8] = {8, 1, 2, 3, 4, 5, 6, 7};
static char direction[2] = {'r','l'};

// Version info string
static char versioninfo[] = "Starscream v" VERSION " (built " __DATE__ ")";

/* Output file where code will be emitted */
static FILE *codefile;

/* Line number - used to make temporary labels i.e. "ln1234" */
static int linenum;

/* Context size */
static int contextsize = 0;

/* Effective address modes */
enum eamode {
  dreg, areg, aind, ainc, adec, adsp,
  axdp, absw, absl, pcdp, pcxd, immd
};

/* Loop information (68010) */
static int loop_c_cycles;
static int loop_t_cycles;
static int loop_x_cycles;
static unsigned char loopinfo[0x10000];

/*
** Misc. global variables which are used while generating instruction
** handling routines.  Some of these may assume more than one role.
*/
static enum eamode main_eamode;   /* EA mode, usually source */
static enum eamode main_destmode; /* EA mode, destination (for MOVE) */
static int main_size;             /* Operand size (1, 2, or 4) */
static int sizedef;               /* Size field in instruction word */
static int main_reg;              /* Register number */
static int main_cc;               /* Condition code (0-F) */
static int main_dr;               /* Direction (right or left) */
static int main_ir;               /* Immediate or register (for shifts) */
static int main_qv;               /* Quick value */

/* Emit a line of code (format string with other junk) */
static void emit(const char *fmt, ...) {
  va_list a;
  va_start(a, fmt);
  if(codefile) {
    vfprintf(codefile, fmt, a);
  } else {
    fprintf(stderr, "Bad news: Tried to emit() to null file\n");
    exit(1);
  }
}

/* Dump all options.  This is delivered to stderr and to the code file. */
static void optiondump(FILE *o, char *prefix) {
  fprintf(o, "%sCPU type: %d (%d-bit addresses)\n", prefix,
    cputype, addressbits);
  fprintf(o, "%sIdentifiers begin with \"%s\"\n", prefix,
    sourcename);
  fprintf(o, "%s%s calling conventions\n", prefix,
    use_stack ? "__cdecl" : "__fastcall");
  fprintf(o, "%sHog mode: %s\n", prefix,
    hog ? "On" : "Off");
}

static void gen_banner(void) {
  emit("; Generated by STARSCREAM version " VERSION "\n");
  emit("; For assembly by NASM only\n");
  emit(";\n");
  emit("; Options:\n");
  optiondump(codefile, "; *  ");
  emit(";\n");
  emit("bits 32\n");
}

static void align(int n) {
  emit("times ($$-$)&%d db 0\n", n - 1);
}

static void maskaddress(char *reg) {
  if(addressbits < 32) {
    emit("and %s,%d\n", reg, (1 << addressbits) - 1);
  }
}

static void begin_source_proc(const char *fname, int params) {
  char s[1000];
  // generate properly mangled name for the calling convention
  if(use_stack) {
    sprintf(s, "_%s_%s", sourcename, fname);
  } else {
    sprintf(s, "@%s_%s@%d", sourcename, fname, 4 * params);
  }
  emit("global %s\n", s);
  emit("%s:\n", s);
}

static void emitvar(const char *name, int align, int size) {
  if(align) {
    contextsize += (align-1);
    contextsize &= ~(align-1);
  }
  emit("%s equ %d\n", name, contextsize);
  contextsize += size;
}

/* Generate variables */
static void gen_variables(void) {
  /*
  ** CONTEXTINFO_MEM16
  ** CONTEXTINFO_MEM16FC
  **
  ** 16-bit memory interface
  */
  if(cputype <= 68010) {
    emitvar("__fetch"       ,4,4);
    emitvar("__readbyte"    ,4,4);
    emitvar("__readword"    ,4,4);
    emitvar("__writebyte"   ,4,4);
    emitvar("__writeword"   ,4,4);
    emitvar("__s_fetch"     ,4,4);
    emitvar("__s_readbyte"  ,4,4);
    emitvar("__s_readword"  ,4,4);
    emitvar("__s_writebyte" ,4,4);
    emitvar("__s_writeword" ,4,4);
    emitvar("__u_fetch"     ,4,4);
    emitvar("__u_readbyte"  ,4,4);
    emitvar("__u_readword"  ,4,4);
    emitvar("__u_writebyte" ,4,4);
    emitvar("__u_writeword" ,4,4);
    if(cputype == 68010) {
      emitvar("__f_readbyte" ,4,4);
      emitvar("__f_readword" ,4,4);
      emitvar("__f_writebyte",4,4);
      emitvar("__f_writeword",4,4);
    }
  /*
  ** CONTEXTINFO_MEM32
  **
  ** 32-bit memory interface
  */
  } else {
    emitvar("__fetch"     ,4,4);
    emitvar("__readbus"   ,4,4);
    emitvar("__writebus"  ,4,4);
    emitvar("__s_fetch"   ,4,4);
    emitvar("__s_readbus" ,4,4);
    emitvar("__s_writebus",4,4);
    emitvar("__u_fetch"   ,4,4);
    emitvar("__u_readbus" ,4,4);
    emitvar("__u_writebus",4,4);
    emitvar("__f_readbus" ,4,4);
    emitvar("__f_writebus",4,4);
  }
  /*
  ** CONTEXTINFO_COMMON
  **
  ** Registers and other info common to all CPU types
  **
  ** It should be noted that on a double fault, bit 0 of both __pc and
  ** __interrupts will be set to 1.
  */
  if(cputype >= 68000) {
    emitvar("__hwstate",4,4);
    emitvar("__resethandler",4,4);
    emitvar("__reg",4,0);
    emitvar("__dreg",4,8*4);
    emitvar("__areg",4,7*4);
    emitvar("__a7",4,4);
    emitvar("__asp",4,4);
    emitvar("__pc",4,4);
    emitvar("__odometer",4,4);
    /* Bit 0 of __interrupts = stopped state */
    emitvar("__interrupts",1,8);
    emitvar("__sr",2,2);
  }
  /*
  ** CONTEXTINFO_68010
  **
  ** Registers used on the 68010 and higher
  */
  if(cputype >= 68010) {
    emitvar("__sfc",1,1);
    emitvar("__dfc",1,1);
    emitvar("__vbr",4,4);
    emitvar("__bkpthandler",4,4);
  }
  /*
  ** CONTEXTINFO_68010SPECIFIC
  **
  ** Registers used only on the 68010
  */
  if(cputype == 68010) {
    emitvar("__loopmode",1,1);
  }
  /*
  ** CONTEXTINFO_68020
  **
  ** Registers used on the 68020 and higher
  */
  if(cputype >= 68020) {
    /*
    ** 68020 stack pointer rules (tentative)
    **
    ** First of all, the 68000/68010 stack pointer behavior has
    ** not changed:
    **
    ** 1. In supervisor mode, __a7 contains the supervisor stack
    **    pointer and __asp contains the user stack pointer.
    ** 2. In user mode, __a7 contains the user stack pointer and
    **    __asp contains the supervisor stack pointer.
    **
    ** The only difference is that the "supervisor stack pointer"
    ** can be either ISP or MSP.  __xsp contains whichever stack
    ** pointer is _not_ the current "supervisor stack pointer".
    **
    ** Here's a table summarizing the above rules:
    **
    **   S M | __a7 __asp __xsp
    **   ----+-----------------
    **   0 0 |  USP   ISP   MSP
    **   0 1 |  USP   MSP   ISP
    **   1 0 |  ISP   USP   MSP
    **   1 1 |  MSP   USP   ISP
    **
    ** As usual, whenever SR changes, we have to play Stack
    ** Pointer Switcheroo:
    **
    **  * If S changes: swap __asp and __a7 (as usual)
    **  * If M changes:
    **    - If S=0, swap __xsp and __asp
    **    - If S=1, swap __xsp and __a7
    */
    emitvar("__xsp",4,4);
  }
  emitvar("__cycles_needed",4,4);
  emitvar("__cycles_leftover",4,4);
  emitvar("__fetch_region_start",4,4); // Fetch region cache
  emitvar("__fetch_region_end",4,4);
  emitvar("__xflag",1,1);
  /*
  **  Format of __execinfo:
  **  Bit 0:  s680x0exec currently running
  **  Bit 1:  PC out of bounds
  **  Bit 2:  Special I/O section
  **
  ** "Special I/O section" is enabled during group 0 exception
  ** processing, and it means a couple things:
  **   * Address and bus errors will not be tolerated (the CPU will
  **     just keel over and die).  Therefore, information such as the
  **     current PC is not relevant.
  **   * Registers are not necessarily live.  Since special I/O
  **     sections are guaranteed not to cause exceptions, this is not a
  **     problem.
  */
  emitvar("__execinfo",1,1);
  emitvar("__trace_trickybit",1,1); // Pending trace exception
  emitvar("__io_cycle_counter",4,4); // always -1 when idle
  emitvar("__io_fetchbase",4,4);
  emitvar("__io_fetchbased_pc",4,4);
  emitvar("__access_address",4,4);
  emitvar("save_01",4,4); // Stef Fix (Gens)

  emitvar("contexttop",4,0);
}

/* Prepare to leave into the cold, dark world of compiled C code */
static void airlock_exit(void) {
  emit("mov [ebp+__io_cycle_counter],edi\n");
  emit("mov [ebp+__io_fetchbased_pc],esi\n");
  emit("push ebx\n");
  emit("push eax\n");
}

/* Prepare to return to the warm fuzzy world of assembly code
** (where everybody knows your name) */
static void airlock_enter(void) {
  emit("pop eax\n");
  emit("pop ebx\n");
  emit("mov edi,[ebp+__io_cycle_counter]\n");
  emit("mov esi,[ebp+__io_fetchbased_pc]\n");
}

enum { airlock_stacksize = 8 };

static void cache_ccr(void) {
  emit("mov al,[ebp+__sr]\n");    /* read CCR -> AL */                 /* ????????000XNZVC */
  emit("mov ah,al\n");        /* copy to AH */                     /* 000XNZVC000XNZVC */
  emit("and ax,0C10h\n");     /* isolate NZ...X */                 /* 0000NZ00000X0000 */
  emit("shl ah,3\n");       /* put NZ almost where we want it */   /* 0NZ00000000X0000 */
  emit("shr al,4\n");         /* shift X flag into bit 0 */        /* 0NZ000000000000X */
  emit("mov [ebp+__xflag],al\n"); /* store X flag */                   /* 0NZ000000000000X al -> xflag */
  emit("mov al,[ebp+__sr]\n");    /* read CCR -> AL again */           /* 0NZ00000000XNZVC */
  emit("and al,3\n");         /* isolate VC */                     /* 0NZ00000000000VC */
  emit("shr al,1\n");         /* just V */                         /* 0NZ000000000000V carry */
  emit("adc ah,ah\n");        /* append C to rest of flags */      /* NZ00000C0000000V */
}

static void writeback_ccr(void) {
  emit("shr ah,1\n");         /* C flag -> x86 carry */            /* 0NZ?????0000000V carry */
  emit("adc ax,ax\n");        /* append to V flag */               /* NZ?????0000000VC */
  emit("and ax,0C003h\n");    /* isolate NZ.......VC */            /* NZ000000000000VC */
  emit("or ah,[ebp+__xflag]\n");  /* load X flag */                    /* NZ00000X000000VC */
  emit("ror ah,4\n");         /* now we have XNZ....VC */          /* 000XNZ00000000VC */
  emit("or al,ah\n");         /* OR them together */               /* 000XNZ00000XNZVC */
  emit("mov [ebp+__sr],al\n");    /* store the result */               /* 000XNZ00000XNZVC al -> sr */
}

/*
** This will make edi _very_ negative... far enough negative that the
** leftover cycle incorporation at the end of s68000exec() shouldn't be
** enough to make it positive.
*/
static void force_context_switch(void) {
  emit("sub edi,[ebp+__cycles_needed]\n");
  emit("mov dword[ebp+__cycles_needed],0\n");
}

/*
** Put all the unused cycles in the leftover cycle bank, so we can call
** attention to the tricky bit processor.
*/
static void force_trickybit_process(void) {
  emit("inc edi\n");
  emit("add [ebp+__cycles_leftover],edi\n");
  emit("or edi,byte -1\n");/* smaller than a mov */
}

/* "newpc" has been renamed to this */
void perform_cached_rebase(void);

/* Copy either __s_* or __u_* memory map pointers */
static void copy_memory_map(char *map, char *reg) {
  emit("mov %s,[ebp+__%s_fetch]\n", reg, map);
  emit("mov [ebp+__fetch],%s\n", reg);
  if(cputype < 68020) {
    emit("mov %s,[ebp+__%s_readbyte]\n" , reg, map);
    emit("mov [ebp+__readbyte],%s\n" , reg);
    emit("mov %s,[ebp+__%s_readword]\n" , reg, map);
    emit("mov [ebp+__readword],%s\n" , reg);
    emit("mov %s,[ebp+__%s_writebyte]\n", reg, map);
    emit("mov [ebp+__writebyte],%s\n", reg);
    emit("mov %s,[ebp+__%s_writeword]\n", reg, map);
    emit("mov [ebp+__writeword],%s\n", reg);
  } else {
    emit("mov %s,[ebp+__%s_readbus]\n"  , reg, map);
    emit("mov [ebp+__readbus],%s\n"  , reg);
    emit("mov %s,[ebp+__%s_writebus]\n" , reg, map);
    emit("mov [ebp+__writebus],%s\n" , reg);
  }
}

/***************************************************************************/

static void gen_interface(void) {
  emit("section .text\n");
  emit("bits 32\n");
  emit("top:\n");

/***************************************************************************/
/*
**  int s680x0_init(void)
**
**  Entry: Nothing
**  Exit:  Zero
**
**  This must be called before anything else.  It decompresses the main jump
**  table (and loop info, in the case of the 68010).
*/
  begin_source_proc("init", 0);

  emit("pushad\n");
  emit("mov edi,_jmptbl\n");
  emit("mov esi,_jmptblcomp\n");
  if(cputype == 68010) {
    emit("mov ebx,_looptbl\n");
  }
  emit(".decomp:\n");
  emit("lodsd\n");
  emit("mov ecx,eax\n");
  emit("and eax,0FFFFFFh\n");
  emit("shr ecx,24\n");
  emit("add eax,top\n");
  emit("inc ecx\n");
  if(cputype == 68010) {
    emit("mov ebp,ecx\n");
  }
  emit(".jloop:\n");
  emit("mov [edi],eax\n");
  emit("add edi,byte 4\n");
  emit("dec ecx\n");
  emit("jnz short .jloop\n");
  if(cputype == 68010) {
    emit("lodsb\n");
    emit(".lloop:\n");
    emit("mov [ebx],al\n");
    emit("inc ebx\n");
    emit("dec ebp\n");
    emit("jnz short .lloop\n");
  }
  emit("cmp edi,_jmptbl+262144\n");
  emit("jne short .decomp\n");

  emit("popad\n");
  emit("xor eax,eax\n");
  emit("ret\n");

/***************************************************************************/
/*
**  const char *s680x0_get_version(void)
**
**  Entry: Nothing
**  Exit:  Pointer to version info string
*/

  emit("versioninfo:\n");
  emit("db \"%s\",0\n", versioninfo);

  begin_source_proc("get_version", 0);

  emit("mov eax,versioninfo\n");
  emit("ret\n");

/***************************************************************************/
/*
**  unsigned s680x0_get_state_size(void)
**
**  Entry: Nothing
**  Exit:  Size of context array (in bytes)
*/
  begin_source_proc("get_state_size", 0);

  emit("mov eax,contexttop\n");
  emit("ret\n");

/***************************************************************************/
/*
**  void s680x0_clear_state(state)
*/
  begin_source_proc("clear_state", 1);

  emit("push ebp\n");
  if(use_stack) {
    emit("mov ebp,[esp+4+4]\n");
  } else {
    emit("mov ebp,ecx\n");
  }

  emit("xor eax,eax\n");
  emit(".clearloop:\n");
  emit("mov dword[ebp+eax],0\n");
  emit("add eax,byte 4\n");
  emit("cmp eax,contexttop\n");
  emit("jb .clearloop\n");

  // remember to init __io_cycle_counter to -1
  emit("mov dword[ebp+__io_cycle_counter],-1\n");
  // also initialize SR just because
  emit("mov word[ebp+__sr],2700h\n");

  emit("pop ebp\n");
  emit("ret\n");

/***************************************************************************/
/*
**  void s680x0_set_memory_maps(state,infostruct)
*/
  begin_source_proc("set_memory_maps", 2);

  emit("push ebp\n");

  if(use_stack) {
    emit("mov ebp,[esp+4+4]\n");
    emit("mov edx,[esp+4+8]\n");
  } else {
    emit("mov ebp,ecx\n");
  }

  emit("mov eax,[edx+0]\n");
  emit("mov [ebp+__s_fetch],eax\n");
  emit("mov eax,[edx+4]\n");
  emit("mov [ebp+__s_readbyte],eax\n");
  emit("mov eax,[edx+8]\n");
  emit("mov [ebp+__s_readword],eax\n");
  emit("mov eax,[edx+12]\n");
  emit("mov [ebp+__s_writebyte],eax\n");
  emit("mov eax,[edx+16]\n");
  emit("mov [ebp+__s_writeword],eax\n");
  emit("mov eax,[edx+20]\n");
  emit("mov [ebp+__u_fetch],eax\n");
  emit("mov eax,[edx+24]\n");
  emit("mov [ebp+__u_readbyte],eax\n");
  emit("mov eax,[edx+28]\n");
  emit("mov [ebp+__u_readword],eax\n");
  emit("mov eax,[edx+32]\n");
  emit("mov [ebp+__u_writebyte],eax\n");
  emit("mov eax,[edx+36]\n");
  emit("mov [ebp+__u_writeword],eax\n");
  // copy the appropriate memory map
  emit("test byte[ebp+__sr+1],20h\n");
  emit("jz short .user\n");
  copy_memory_map("s", "ecx");
  emit("jmp short .return\n");
  emit(".user:\n");
  copy_memory_map("u", "ecx");
  emit(".return:\n");
  emit("pop ebp\n");
  emit("ret\n");

/***************************************************************************/
/*
**  int s680x0_reset(state)
**
**  Entry: Nothing
**  Exit:  0 on success
**         1 on failure:
**           * if there's no Supervisor Program entry for address 0
**           * if s680x0exec() is active
**        -1 on double fault
*/
  begin_source_proc("reset", 1);

  emit("push ebp\n");
  emit("push esi\n");

  if(use_stack) {
    emit("mov ebp,[esp+4+8]\n");
  } else {
    emit("mov ebp,ecx\n");
  }

  emit("mov eax,1\n");
  emit("test [ebp+__execinfo],al\n"); /* Ensure s680x0exec() inactive */
  emit("jnz near .return\n");
  emit("cmp dword[ebp+__s_fetch],0\n");
  emit("je near .return\n");
  emit("dec eax\n");
  emit("mov [ebp+__execinfo],al\n");
  emit("sub eax,byte 16\n");
  emit(".gp:\n");
  emit("mov dword[ebp+__reg+64+eax*4],0\n");
  emit("inc eax\n");
  emit("jnz short .gp\n");
  emit("mov [ebp+__asp],eax\n");
  if(cputype >= 68020) emit("mov [ebp+__xsp],eax\n");
  /* Set up SR for no tracing, supervisor mode, ISP, PPL 7 */
  emit("mov word[ebp+__sr],2700h\n");
  if(cputype >= 68010) {
    emit("mov [ebp+__vbr],eax\n");
    emit("mov [ebp+__sfc],al\n");
    emit("mov [ebp+__dfc],al\n");
  }
  if(cputype == 68010) {
    emit("mov [ebp+__loopmode],al\n");
   }
  /* Copy supervisor address space information */
  copy_memory_map("s", "eax");
  /* Generate Supervisor Program Space reads to get the initial PC and
  ** SSP/ISP */
  emit("mov eax,1\n"); /* assume failure */
  emit("mov [ebp+__pc],eax\n");
  emit("mov [ebp+__interrupts],al\n");
  emit("xor esi,esi\n");
  emit("call basefunction\n");/* will preserve eax */
  emit("test byte[ebp+__execinfo],2\n");
  emit("jnz short .return\n");
  emit("add esi,[ebp+__io_fetchbase]\n");
  emit("mov eax,[esi]\n");
  emit("rol eax,16\n");
  emit("mov [ebp+__a7],eax\n");
  emit("mov eax,[esi+4]\n");
  emit("rol eax,16\n");
  emit("mov [ebp+__pc],eax\n");
  /* An address error here will cause a double fault */
  emit("and eax,byte 1\n");
  emit("mov [ebp+__interrupts],al\n");
  emit("neg eax\n"); /* -1 on double fault, 0 on success */

  emit(".return:\n");
  emit("pop esi\n");
  emit("pop ebp\n");
  emit("ret\n");

/***************************************************************************/
/*
**  unsigned s680x0_execute(state, cycles)
**
**  Entry: state pointer, # cycles to execute
**  Exit:  80000000h:  success
**         80000001h:  PC out of range
**         80000002h:  unsupported stack frame
**         FFFFFFFFh:  CPU is dead because of a double fault
**       < 80000000h:  invalid instruction = address of invalid instr.
*/
  begin_source_proc("execute", 2);

  emit("push ebp\n");
  emit("push ebx\n");
  emit("push ecx\n");
  emit("push edx\n");
  emit("push esi\n");
  emit("push edi\n");

  if(use_stack) {
    emit("mov ebp,[esp+4+24]\n");
    emit("mov eax,[esp+4+28]\n");
  } else {
    emit("mov ebp,ecx\n");
    emit("mov eax,edx\n");
  }

  /*
  ** Check for stopped and double-faulted states.
  */
  emit("test byte[ebp+__interrupts],1\n");
  emit("jz .notstopped\n");
  emit("test byte[ebp+__pc],1\n");
  emit("jz .notfaulted\n");
  emit("or eax,byte -1\n");
  emit("jmp execreturn\n");
  emit(".notfaulted:\n");
  emit("add [ebp+__odometer],eax\n");
  emit("mov eax,80000000h\n");
  emit("jmp execreturn\n");
  emit(".notstopped:\n");

  emit("mov [ebp+__cycles_needed],eax\n");
  emit("mov edi,eax\n");/* store # of cycles to execute */
  emit("dec edi\n");

  emit("xor ebx,ebx\n");
  emit("mov esi,[ebp+__pc]\n");
  cache_ccr();
  emit("mov dword[ebp+__io_fetchbase],0\n");
  emit("mov byte[ebp+__execinfo],1\n");

  /*
  ** Force an uncached re-base.
  ** This fulfills the "Entry to s680x0exec()" case.
  */
  emit("call basefunction\n");
  emit("add esi,[ebp+__io_fetchbase]\n");
  emit("test byte[ebp+__execinfo],2\n"); /* Check for PC out of bounds */
  emit("jnz near exec_bounderror\n");

  emit("mov dword[ebp+__cycles_leftover],0\n");/* an extra precaution */

  /* PPL and Trace checkpoint */
  emit("exec_checkpoint:\n");
  emit("js execquit\n");

  /* Check PPL */
  emit("mov cl,[ebp+__sr+1]\n");
  emit("and ecx,byte 7\n");
  emit("inc ecx\n");
  emit("mov ch,[ebp+__interrupts]\n");
  emit("or ch,ch\n");
  emit("js short .yesint\n");
  emit("shr ch,cl\n");
  emit("jz short .noint\n");
  emit(".yesint:\n");
  emit("call flush_interrupts\n");
  /* Force an uncached re-base */
  emit("call basefunction\n");
  emit("add esi,[ebp+__io_fetchbase]\n");
  emit("test byte[ebp+__execinfo],2\n"); /* Check for PC out of bounds */
  emit("jnz near exec_bounderror\n");
  emit(".noint:\n");

  /*
  ** If the SR Trace flag is set, generate a pending trace exception.
  */
  emit("mov ch,[ebp+__sr+1]\n");
  emit("and ch,80h\n"); /* isolate trace flag */
  emit("mov [ebp+__trace_trickybit],ch\n");
  emit("jz execloop\n");
  /*
  ** Activate the tricky bit processor.
  **
  ** Because edi isn't checked for negativity before entering the
  ** fetch/decode/execute loop, we're guaranteed to execute at least
  ** one more instruction before any trace exception.
  **
  ** If another group 1 exception happens in the course of executing
  ** this next instruction, then the group_1_exception routine will
  ** clear the trace tricky bit and re-adjust the cycle counters, and
  ** we'll pretend none of this ever happened.
  */
  force_trickybit_process();

  emit("execloop:\n");
/*  emit("xor ebx,ebx\n");suffice to say, bits 16-31 should be zero... */
  emit("mov bx,[esi]\n");
  emit("add esi,byte 2\n");
  emit("jmp dword[_jmptbl+ebx*4]\n");
  /* Traditional loop - used when hog mode is off */
  if(!hog) {
    emit("execend:\n");
    emit("jns execloop\n");
  }
  emit("execquit:\n");

  /*
  ** Tricky Bit Processor
  */
  /* Look for trace tricky bit */
  emit("cmp byte[ebp+__trace_trickybit],0\n");
  emit("je short execquit_notrace\n");
  /* Generate trace exception */
  emit("mov edx,24h\n");
  emit("call group_1_exception\n");
  perform_cached_rebase();
  /* Subtract time used by exception processing */
  emit("sub edi,byte %d\n", (cputype == 68010) ? 38 : 34);
  emit("execquit_notrace:\n");

  /*
  ** Look for pending interrupts that exceed the current PPL.  These
  ** are higher priority and are therefore processed last (the ISR will
  ** end up getting control).
  */
  emit("mov cl,[ebp+__sr+1]\n");
  emit("and ecx,byte 7\n");
  emit("inc ecx\n");
  emit("mov ch,[ebp+__interrupts]\n");
  emit("or ch,ch\n");
  emit("js short execquit_yesinterrupt\n");
  emit("shr ch,cl\n");
  emit("jz short execquit_nointerrupt\n");
  emit("execquit_yesinterrupt:\n");
  emit("call flush_interrupts\n");
  /*
  ** Force an uncached re-base.
  ** This fulfills the "Hardware interrupt" case.
  */
  emit("call basefunction\n");
  emit("add esi,[ebp+__io_fetchbase]\n");
  emit("test byte[ebp+__execinfo],2\n"); /* Check for PC out of bounds */
  emit("jnz short exec_bounderror\n");
  emit("execquit_nointerrupt:\n");

  /*
  ** Incorporate leftover cycles (if any) and see if we should keep
  ** running.
  */
  emit("add edi,[ebp+__cycles_leftover]\n");
  emit("mov dword[ebp+__cycles_leftover],0\n");
  emit("jns execloop\n");

  /* Leave s680x0exec with "Success" code. */
  emit("mov ecx,80000000h\n");

  /*
  ** Exit the s680x0exec routine.  By this time the return code should
  ** already be in ecx.
  */
  emit("execexit:\n");
  emit("sub esi,[ebp+__io_fetchbase]\n");
  writeback_ccr();
  emit("mov [ebp+__pc],esi\n");
  emit("inc edi\n");
  emit("mov edx,[ebp+__cycles_needed]\n");
  emit("sub edx,edi\n");
  emit("add [ebp+__odometer],edx\n");
  emit("mov byte[ebp+__execinfo],0\n");

  /*
  ** Remember: __io_cycle_counter is always -1 when idle!
  **
  ** This prevents us from having to check __execinfo during the
  ** readOdometer / tripOdometer calls.
  */
  emit("mov dword[ebp+__cycles_needed],0\n");
  emit("mov dword[ebp+__io_cycle_counter],-1\n");

  emit("mov eax,ecx\n");/* return code */

  emit("execreturn:\n");

  emit("pop edi\n");
  emit("pop esi\n");
  emit("pop edx\n");
  emit("pop ecx\n");
  emit("pop ebx\n");
  emit("pop ebp\n");
  emit("ret\n");

  /*
  ** Leave s680x0exec with "Out of bounds" code.
  */
  emit("exec_bounderror:\n");
  emit("mov ecx,80000001h\n");
  emit("jmp short execexit\n");

  /*
  ** Invalid instruction handler
  */
  emit("invalidins:\n");
  emit("sub esi,byte 2\n");  /* back up one word */
  emit("mov ecx,esi\n");/* get address in ecx */
  emit("sub ecx,[ebp+__io_fetchbase]\n");/* subtract base */
  maskaddress("ecx");
  if(addressbits == 32) {
    emit("and ecx,7FFFFFFFh\n");
  }
/*  emit("or byte[__stopped],2\n");*/
  emit("jmp short execexit\n");

/***************************************************************************/
/*
**  unsigned s680x0_getreg(state,n)
*/
  begin_source_proc("getreg", 2);

  emit("push ebp\n");
  if(use_stack) {
    emit("mov ebp,[esp+4+4]\n");
    emit("mov edx,[esp+4+8]\n");
  } else {
    emit("mov ebp,ecx\n");
  }
  emit("cmp edx,byte 17\n");
  emit("jb short .normalreg\n");
  emit("je short .pc\n");
  emit("cmp edx,byte 18\n");
  emit("je short .sr\n");
  emit("ja short .none\n");

  emit(".normalreg:\n");
  emit("mov eax,[ebp+__reg+edx*4]\n");
  emit("jmp short .exit\n");

  emit(".pc:\n");
  /*
  ** Returns the current program counter.  Works anywhere, including I/O,
  ** RESET, and BKPT handlers.
  **
  ** Note that the value returned won't necessarily coincide exactly with the
  ** beginning of an instruction.
  */
  emit("test byte[ebp+__execinfo],1\n");
  emit("jnz short .pclive\n");
  emit("mov eax,[ebp+__pc]\n");
  emit("jmp short .exit\n");
  emit(".pclive:\n");
  emit("mov eax,[ebp+__io_fetchbased_pc]\n");
  emit("sub eax,[ebp+__io_fetchbase]\n");
  emit("jmp short .exit\n");

  emit(".sr:\n");
  emit("movzx eax,word[ebp+__sr]\n");
  emit("jmp short .exit\n");

  emit(".none:\n");
  emit("xor eax,eax\n");
  emit(".exit:\n");
  emit("pop ebp\n");
  emit("ret\n");

/***************************************************************************/
/*
**  s680x0_interrupt(state, 256*vector+level)
**
**  Entry: interrupt level
**         256*vector (-1 for auto, -2 for spurious)
**  Exit:  EAX = 0 on success
**               1 on failure, previous vector exists
**               2 on invalid input
*/
  begin_source_proc("interrupt", 2);

  emit("push ebp\n");
  emit("push ecx\n");
  emit("push edx\n");
  if(use_stack) {
    emit("mov ebp,[esp+4+12]\n"); /* state */
    emit("mov edx,[esp+8+12]\n"); /* vector*256+level */
  } else {
    emit("mov ebp,ecx\n");
  }
  emit("mov eax,edx\n");
  emit("sar edx,8\n"); // edx=vector
  emit("and eax,0FFh\n"); // eax=level
  /*
  ** Verify parameters.
  */
  emit("cmp eax,byte 7\n");
  emit("ja short .badinput\n");
  emit("or eax,eax\n");
  emit("jz short .badinput\n");
  emit("cmp edx,255\n");
  emit("jg short .badinput\n");
  emit("cmp edx,byte -2\n");
  emit("jl short .badinput\n");
  /*
  ** Calculate the vector number.
  */
  emit("jne short .notspurious\n");
  emit("mov edx,18h\n");
  emit(".notspurious:\n");
  emit("or edx,edx\n");
  emit("jns short .notauto\n");
  emit("lea edx,[eax+18h]\n");
  emit(".notauto:\n");
  /*
  ** Test to see if this interrupt level is already pending.
  ** If it is, return with failure.
  */
  emit("push ecx\n");
  emit("mov cl,al\n");
  emit("mov ah,1\n");
  emit("shl ah,cl\n");
  emit("pop ecx\n");
  emit("test [ebp+__interrupts],ah\n");
  emit("jnz .failure\n");
  /*
  ** Commit the given interrupt and vector number.
  */
  emit("or [ebp+__interrupts],ah\n");
  emit("mov ah,0\n");
  emit("mov [ebp+__interrupts+eax],dl\n");
  emit("and byte[ebp+__interrupts],0FEh\n");
  /*
  ** Notify the tricky bit handler.  If we're doing this outside of
  ** s680x0exec(), then the notification will have no effect, because
  ** __io_cycle_counter is always -1 when idle.
  */
  emit("mov edx,[ebp+__io_cycle_counter]\n");
  emit("inc edx\n");
  emit("add [ebp+__cycles_leftover],edx\n");
  emit("mov dword[ebp+__io_cycle_counter],-1\n");
  /*
  ** Success (0)
  */
  emit("pop edx\n");
  emit("pop ecx\n");
  emit("pop ebp\n");
  emit("xor eax,eax\n");
  emit("ret\n");
  /*
  ** Failure (1)
  */
  emit(".failure:\n");
  emit("pop edx\n");
  emit("pop ecx\n");
  emit("pop ebp\n");
  emit("mov eax,1\n");
  emit("ret\n");
  /*
  ** Bad input (2)
  */
  emit(".badinput:\n");
  emit("pop edx\n");
  emit("pop ecx\n");
  emit("pop ebp\n");
  emit("mov eax,2\n");
  emit("ret\n");

/***************************************************************************/
/*
**  s680x0_flush_interrupts(state)
**
**  Flushes all pending interrupts.
**
**  Entry: Nothing
**  Exit:  Nothing
*/
  begin_source_proc("flush_interrupts", 1);

  emit("push ebp\n");
  if(use_stack) {
    emit("mov ebp,[esp+4+4]\n");
  } else {
    emit("mov ebp,ecx\n");
  }

  /*
  ** If s680x0exec() is already running, then the interrupts are about
  ** to get flushed anyway.  So ignore this call.
  */
  emit("test byte[ebp+__execinfo],1\n");
  emit("jnz .noflush\n");
  /* Make registers "live" */
  emit("pushad\n");
  emit("mov esi,[ebp+__pc]\n");
  emit("mov dword[ebp+__io_fetchbase],0\n");
  cache_ccr();
  emit("xor edi,edi\n"); /* well, semi-live */
  emit("call flush_interrupts\n");
  emit("sub [ebp+__odometer],edi\n"); /* edi will be <= 0 here */
  emit("mov [ebp+__pc],esi\n"); /* PC guaranteed unbased */
  writeback_ccr();
  emit("popad\n");
  emit(".noflush:\n");

  emit("pop ebp\n");
  emit("ret\n");

/***************************************************************************/
/*
**  unsigned s680x0_read_odometer(state)
**
**  Reads the odometer (works from anywhere)
**  Entry: Nothing
**  Exit:  Odometer in EAX
*/
  begin_source_proc("read_odometer", 1);

  emit("push ebp\n");
  if(use_stack) {
    emit("mov ebp,[esp+4+4]\n");
  } else {
    emit("mov ebp,ecx\n");
  }

  emit("mov eax,[ebp+__cycles_needed]\n");
  emit("sub eax,[ebp+__io_cycle_counter]\n");
  emit("dec eax\n");               /* eax = elapsed cycles */
  emit("sub eax,[ebp+__cycles_leftover]\n");
  emit("add eax,[ebp+__odometer]\n");  /* add to old __odometer */

  emit("pop ebp\n");
  emit("ret\n");

/***************************************************************************/
/*
**  s680x0_break(state)
**
**  Ends the s680x0exec call prematurely.  The early exit is reflected in
**  __odometer.
**  Entry: Nothing
**  Exit:  Nothing
*/
  begin_source_proc("break", 1);

  emit("push ebp\n");
  if(use_stack) {
    emit("mov ebp,[esp+4+4]\n");
  } else {
    emit("mov ebp,ecx\n");
  }

  emit("mov eax,[ebp+__cycles_needed]\n");
  emit("sub [ebp+__io_cycle_counter],eax\n");
  emit("xor eax,eax\n");
  emit("mov [ebp+__cycles_needed],eax\n");

  emit("pop ebp\n");
  emit("ret\n");


}

/***************************************************************************/

/*
** Routine that flushes pending interrupts (with correct priority).
** Assumes "live" registers, including EDI and [__io_fetchbase].
**
** Does not rebase the PC.  In fact, it will un-base the PC even if there
** aren't any pending interrupts.
**
** s680x0flushInterrupts() is actually a wrapper for this.
*/
static void gen_flush_interrupts(void) {
  int cycles = (cputype == 68010) ? 46 : 44;
  emit("flush_interrupts:\n");
  /* Unbase PC */
  emit("sub esi,[ebp+__io_fetchbase]\n");
  emit("mov dword[ebp+__io_fetchbase],0\n");
  /* This loop is intentionally post-tested because interrupt level 7
  ** is non-maskable. */
  emit("mov edx,7\n");
  emit("mov cl,80h\n");
  emit("mov ch,[ebp+__sr+1]\n"); /* current PPL */
  emit("and ch,7\n");
  emit(".loop:\n");
  emit("test [ebp+__interrupts],cl\n");
  emit("jz short .noint\n");

emit("mov [ebp+save_01], dl\n");      // Stef Fix (Gens)

  emit("mov dl,[ebp+__interrupts+edx]\n");
  emit("not cl\n");
  emit("and [ebp+__interrupts],cl\n");
  emit("shl edx,2\n");
  emit("call group_1_exception\n");

emit("and [ebp+__sr + 1], byte 0xF8\n");  // Stef Fix (Gens)
emit("mov dl, [ebp+save_01]\n");      // Stef Fix (Gens)

  emit("sub edi,byte %d\n", cycles);

emit("or [ebp+__sr + 1], dl\n");      // Stef Fix (Gens)

  emit("jmp short .intdone\n");
  emit(".noint:\n");
  emit("dec edx\n");
  emit("jz short .intdone\n");
  emit("shr cl,1\n");
  emit("cmp dl,ch\n");
  emit("jg short .loop\n");
  emit(".intdone:\n");
  emit("ret\n");
}

/***************************************************************************/

static void ret_timing(int n) {
  if(n) {
    emit("sub edi,%s%d\n", (n < 128) ? "byte " : "", n);
  } else {
    emit("or edi,edi\n");
  }
  /* If hog mode is off, jump back to the main loop */
  if(!hog) {
    emit("jmp execend\n");
  /* If hog mode is on, fetch and execute the next instruction */
  } else {
    emit("js near execquit\n");
    emit("mov bx,[esi]\n");
    emit("add esi,byte 2\n");
    emit("jmp dword[_jmptbl+ebx*4]\n");
  }
}

/*
** This version of ret_timing does a trace flag check.
**
** Note: this only needs to be used for instructions which can potentially
**  _set_ the trace flag.  Instructions which can't set the trace flag (even
**  if they can clear it) are OK to use ret_timing as usual.  Why?  Well, if
**  an instruction is run in trace mode, that instruction is doomed to be
**  traced, regardless if it clears the trace flag during its execution.
**  Furthermore, the trace exception (being a group 1 exception after all)
**  will clear the trace tricky bit as well as the trace flag.
*/
static void ret_timing_checkpoint(int n) {
  if(n) {
    emit("sub edi,%s%d\n", (n < 128) ? "byte " : "", n);
  } else {
    emit("or edi,edi\n");
  }
  emit("jmp exec_checkpoint\n");
}

/***************************************************************************/

/* This routine decodes an extension word into EDX. */
static void gen_decode_ext(void) {
  emit("decode_ext:\n");
  if(cputype <= 68010) {
    emit("push ecx\n");
    emit("movzx edx,word[esi]\n");
    emit("movsx ecx,dl\n");
    emit("add esi,byte 2\n");
    emit("shr edx,12\n");
    emit("mov edx,[ebp+__reg+edx*4]\n");
    emit("jc short .long\n");
    emit("movsx edx,dx\n");
    emit(".long:\n");
    emit("add edx,ecx\n");
    emit("pop ecx\n");
    emit("ret\n");
  } else {
    /* For future expansion... */
    /* need an extra jump table here */
  }
}

/***************************************************************************/

/*
** Perform a cached rebase
*/
void perform_cached_rebase(void) {
  int myline = linenum; linenum += 2;
  emit("cmp esi,[ebp+__fetch_region_start]\n");
  emit("jb short ln%d\n", myline);
  emit("cmp esi,[ebp+__fetch_region_end]\n");
  emit("jbe short ln%d\n", myline + 1);
  emit("ln%d:\n", myline);
  emit("call basefunction\n");
  emit("ln%d:\n", myline + 1);
  emit("add esi,[ebp+__io_fetchbase]\n");
}

/***************************************************************************/

/*
** This is the function that generates a base for a given 68K PC.
**
**  Entry: 68K PC in ESI
**  Exit:  Newly calculated base in [__io_fetchbase]
**
** Sounds like a simple lookup into the __fetch array, and in the case of
** 32-bit addresses, it is.  But for anything less, we'll need to compensate
** for potential garbage in the unused address bits, by subtracting the value
** of these unused bits from the base.  This way the full 32 bits of the PC
** are preserved, even if they're not used.
**
** The only registers which need to be "live" here are ESI and
** [__io_fetchbase].  The fetch region cache is updated, and bit 1 of
** __execinfo is set if the base couldn't be calculated.
*/
static void gen_basefunction(void) {
  emit("basefunction:\n");
  emit("push ecx\n");
  /*
  ** Prepare ESI by masking off unused address bits (but save it
  ** first).
  */
  if(addressbits < 32) {
    emit("push esi\n");
    maskaddress("esi");
  }
  emit("mov ecx,[ebp+__fetch]\n");
  emit(".check:\n");
  emit("cmp esi,[ecx]\n");           /* Are we smaller? */
  emit("jb short .next\n");          /* Yes, go to next address */
  emit("cmp esi,[ecx+4]\n");         /* Are we bigger? */
  emit("jbe short .base\n");
  emit(".next:\n");
  emit("cmp dword [ecx],byte -1\n"); /* End of list? */
  emit("je short .outofrange\n");
  emit("add ecx,byte 12\n");         /* To the next structure */
  emit("jmp short .check\n");
  /* Bad news... we jumped out into the weeds. */
  emit(".outofrange:\n");
  if(addressbits < 32) emit("pop esi\n");
  emit("mov dword[ebp+__io_fetchbase],0\n");
  emit("mov dword[ebp+__fetch_region_start],-1\n");
  emit("mov dword[ebp+__fetch_region_end],0\n");
  force_context_switch();
  emit("or byte[ebp+__execinfo],2\n");
  emit("pop ecx\n");
  emit("ret\n");

  emit(".base:\n");
  /*
  ** Dealing with addressbits < 32 again... if the unused PC bits are
  ** anything but zero, then we'll need to adjust the base to
  ** compensate.
  */
  if(addressbits < 32) {
    emit("mov esi,[esp]\n");
    emit("and esi,%d\n", 0xFFFFFFFF << addressbits);
  }
  emit("push edx\n");
  emit("mov edx,ecx\n");
  /*
  ** Update the fetch region cache, adding in the garbage bits where
  ** applicable.
  */
  emit("mov ecx,[edx]\n");
  if(addressbits < 32) emit("or ecx,esi\n");
  emit("mov [ebp+__fetch_region_start],ecx\n");
  emit("mov ecx,[edx+4]\n");
  if(addressbits < 32) emit("or ecx,esi\n");
  emit("mov [ebp+__fetch_region_end],ecx\n");
  emit("mov ecx,[edx+8]\n");
  emit("mov [ebp+__io_fetchbase],ecx\n");
  emit("pop edx\n");
  if(addressbits < 32) {
    /*
    ** Subtract garbage bits from the base, and restore the
    ** original 32-bit PC value.
    */
    emit("sub [ebp+__io_fetchbase],esi\n");
    emit("pop esi\n");
  }
  emit("pop ecx\n");
  emit("ret\n");
}

/***************************************************************************/

/* Read flags from CL into our CCR.  CX is unmodified. */
static void cl2ccr(void){
  emit("mov al,cl\n");        /* read CCR -> AL */                 /* ???????????XNZVC */
  emit("mov ah,al\n");        /* copy to AH */                     /* ???XNZVC???XNZVC */
  emit("and ax,0C10h\n");     /* isolate NZ...X */                 /* 0000NZ00000X0000 */
  emit("shl ah,3\n");       /* put NZ almost where we want it */   /* 0NZ00000000X0000 */
  emit("shr al,4\n");         /* shift X flag into bit 0 */        /* 0NZ000000000000X */
  emit("mov [ebp+__xflag],al\n"); /* store X flag */                   /* 0NZ000000000000X al -> xflag */
  emit("mov al,cl\n");        /* read CCR -> AL again */           /* 0NZ00000000XNZVC */
  emit("and al,3\n");         /* isolate VC */                     /* 0NZ00000000000VC */
  emit("shr al,1\n");         /* just V */                         /* 0NZ000000000000V carry */
  emit("adc ah,ah\n");        /* append C to rest of flags */      /* NZ00000C0000000V */
}

/*
** Read flags from CX into our SR, performing a mode switch where applicable.
** CX is unmodified.  Uses 4 bytes of stack.
**
** This does not do any of the trace flag mojo, so be sure to check for it
** explicitly where applicable (hint: ret_timing_checkpoint).
*/
static void cx2sr(void){
  int myline = linenum; linenum += 2;

  emit("push ecx\n");
  /* Step 1: switch supervisor mode */
  /* Is the new mode different from the last? */
  emit("mov cl,[ebp+__sr+1]\n");
  emit("and cx,2020h\n");
  emit("xor ch,cl\n");
  emit("jz near ln%d\n", myline);
  /* If so, swap stack pointers */
  emit("mov ecx,[ebp+__a7]\n");
  emit("xchg ecx,[ebp+__asp]\n");
  emit("mov [ebp+__a7],ecx\n");
  /* and copy the appropriate memory map */
  emit("test byte[esp+1],20h\n");
  emit("jz short ln%d\n", myline + 1);
  copy_memory_map("s", "ecx");
  emit("jmp short ln%d\n", myline);
  emit("ln%d:\n", myline + 1);
  copy_memory_map("u", "ecx");
  emit("ln%d:\n", myline);
  emit("pop ecx\n");

  /* Step 2: set new PPL / supervisor mode / trace flag */
  emit("mov [ebp+__sr+1],ch\n");
  emit("and byte[ebp+__sr+1],0A7h\n");

  /* Step 3: Store CL into CCR */
  cl2ccr();
}

/* Read flags from our CCR into CL.  CH is zeroed. */
static void ccr2cl(void){
  /* Read XNZ */
  emit("mov ch,[ebp+__xflag]\n");  /* 0000000X???????? */
  emit("mov cl,ah\n");         /* 0000000XNZ?????C */
  emit("shr cx,6\n");          /* 0000000000000XNZ */
  /* Read V */
  emit("add cl,cl\n");         /* 000000000000XNZ0 */
  emit("or cl,al\n");          /* 000000000000XNZV */
  /* Read C */
  emit("mov ch,ah\n");         /* NZ?????C0000XNZV */
  emit("shl ch,8\n");          /* 000000000000XNZV carry */
  emit("adc cl,cl\n");         /* 00000000000XNZVC */
}

/* Read flags from our SR into CX. */
static void sr2cx(void){
  /* Condition codes */
  ccr2cl();
  /* PPL / supervisor mode / trace flag */
  emit("mov ch,[ebp+__sr+1]\n");
}

/* Switch to supervisor mode.  Can potentially destroy ECX. */
static void supervisor(void){
  int myline=linenum;linenum++;
  emit("test byte[ebp+__sr+1],20h\n");
  emit("jnz short ln%d\n",myline);
  emit("mov ecx,[ebp+__a7]\n");
  emit("xchg ecx,[ebp+__asp]\n");
  emit("mov [ebp+__a7],ecx\n");
  copy_memory_map("s", "ecx");
  emit("or byte[ebp+__sr+1],20h\n");
  emit("ln%d:\n",myline);
}

/***************************************************************************/

static void gen_readbw(int size){
  char z='x';
  if(size==1)z='b';
  if(size==2)z='w';

  align(16);
  emit("readmemory%s:\n",sizename[size]);

  emit("mov [ebp+__access_address],edx\n");
  maskaddress("edx");
  emit("mov ecx,[ebp+__read%s]\n",sizename[size]);
  emit("read%c_check:\n",z);
  emit("cmp edx,[ecx]\n"); /* Are we smaller? */
  emit("jb short read%c_next\n",z); /* Yes, go to next address */
  emit("cmp edx,[ecx+4]\n"); /* Are we bigger? */
  emit("jbe short read%c_call\n",z);
  emit("read%c_next:\n",z);
  emit("cmp dword[ecx],byte -1\n");/* End of list? */
  emit("je short read%c_outofrange\n",z);
  emit("add ecx,byte 16\n");   /* To the next structure */
  emit("jmp short read%c_check\n",z);

  emit("read%c_outofrange:\n",z);
  emit("or ecx,byte -1\n");
  emit("mov edx,[ebp+__access_address]\n");
  emit("ret\n");

  emit("read%c_call:\n",z);
  emit("cmp dword[ecx+8],byte 0\n");
  emit("jne short read%c_callio\n",z);
  emit("sub edx,[ecx]\n");
  emit("add edx,[ecx+12]\n");
  if(size==1){
    emit("xor edx,byte 1\n");
    emit("mov cl,[edx]\n");
  }else{
    emit("mov cx,[edx]\n");
  }
  emit("mov edx,[ebp+__access_address]\n");
  emit("ret\n");

  emit("read%c_callio:\n",z);
  airlock_exit();
  // edx=address
  emit("mov eax,ecx\n"); // eax=pointer to structure (for my purposes)
  emit("mov ecx,[ecx+12]\n"); // userdata
  if(use_stack){
    emit("push edx\n");
    emit("push ecx\n");
  }
  emit("call dword[eax+8]\n");
  if(use_stack)emit("add esp,byte 8\n");
  emit("mov ecx,eax\n");
  airlock_enter();
  emit("mov edx,[ebp+__access_address]\n");
  emit("ret\n");
}

static void gen_readl(void){
  align(16);
  emit("readmemory%s:\n",sizename[4]);
  emit("call readmemoryword\n");
  emit("shl ecx,16\n");
  emit("push ecx\n");
  emit("add edx,byte 2\n");
  emit("call readmemoryword\n");
  emit("sub edx,byte 2\n");
  emit("and ecx,0FFFFh\n");
  emit("or ecx,[esp]\n");
  emit("add esp,byte 4\n");
  emit("ret\n");
}

static void gen_writebw(int size){
  char z='x';
  if(size==1)z='b';
  if(size==2)z='w';

  align(16);
  emit("writememory%s:\n",sizename[size]);

  emit("mov [ebp+__access_address],edx\n");
  emit("push ecx\n");
  emit("write%c_top:\n",z);
  maskaddress("edx");
  emit("mov ecx,[ebp+__write%s]\n",sizename[size]);
  emit("write%c_check:\n",z);
  emit("cmp edx,[ecx]\n"); /* Are we smaller? */
  emit("jb short write%c_next\n",z); /* Yes, go to next address */
  emit("cmp edx,[ecx+4]\n"); /* Are we bigger? */
  emit("jbe short write%c_call\n",z);
  emit("write%c_next:\n",z);
  emit("cmp dword[ecx],byte -1\n");/* End of list? */
  emit("je short write%c_end\n",z);
  emit("add ecx,byte 16\n");   /* To the next structure */
  emit("jmp short write%c_check\n",z);
  emit("write%c_call:\n",z);
  emit("cmp dword[ecx+8],byte 0\n");
  emit("jne short write%c_callio\n",z);
  emit("sub edx,[ecx]\n");
  emit("add edx,[ecx+12]\n");
  if(z=='b')emit("xor edx,byte 1\n");
  emit("pop ecx\n");
  emit("mov [edx],%s\n",x86cx[size]);
  emit("mov edx,[ebp+__access_address]\n");
  emit("ret\n");

  emit("write%c_callio:\n",z);
  /* --- we have:
  ** index in ecx
  ** address in edx
  ** value in [esp]
  ** --- we want:
  ** index in anything (saved first)
  ** address in eax
  ** value in edx
  */
  airlock_exit();
  emit("mov ebx,ecx\n");      /* pointer to structure */
  emit("mov eax,edx\n");      /* address */
  emit("xor edx,edx\n");      /* data */
  emit("mov %s,[esp+%d]\n",x86dx[size],airlock_stacksize);
  if(use_stack){
    emit("push edx\n");
    emit("push eax\n");
    emit("push dword[ebx+12]\n");
  } else {
    emit("push edx\n");
    emit("mov edx,eax\n");
    emit("mov ecx,[ebx+12]\n");
  }
  emit("call dword[ebx+8]\n");
  if(use_stack)emit("add esp,byte 12\n");
  airlock_enter();

  emit("write%c_end:\n",z);
  emit("pop ecx\n");
  emit("mov edx,[ebp+__access_address]\n");
  emit("ret\n");
}

static void gen_writel(void){
  align(16);
  emit("writememory%s:\n",sizename[4]);
  emit("push ecx\n");
  emit("shr ecx,16\n");
  emit("call writememoryword\n");
  emit("mov ecx,[esp]\n");
  emit("and ecx,0FFFFh\n");
  emit("add edx,byte 2\n");
  emit("call writememoryword\n");
  emit("sub edx,byte 2\n");
  emit("pop ecx\n");
  emit("ret\n");
}

/***************************************************************************/
/*
** Group 1 and 2 exceptions
** Exception address is passed in EDX
**
** Does not fix the new PC!
*/
static void gen_group_12_exception(void) {
  emit("group_1_exception:\n");
  emit("group_2_exception:\n");
  emit("and byte[ebp+__interrupts],0FEh\n"); /* first thing's first */
  if(cputype == 68010) {
    emit("mov byte[ebp+__loopmode],0\n");
  }
  if(cputype >= 68010) {
    emit("push edx\n");
    emit("add edx,[ebp+__vbr]\n");
  }
  emit("call readmemorydword\n");
  if(cputype >= 68010) {
    emit("pop edx\n");
  }
  emit("push ecx\n");/* dest. PC */
  sr2cx();
  emit("push ecx\n");/* old SR */
  supervisor();
  /*
  ** Exception handlers do not like being traced, so clear the SR trace
  ** flag as well as the trace tricky bit.
  **
  ** Leave the cycle leftover count alone, in case we still need to
  ** call attention to other unrelated tricky bits.
  */
  emit("and byte[ebp+__sr+1],27h\n");
  emit("mov byte[ebp+__trace_trickybit],0\n");

  emit("mov ecx,esi\n");
  emit("sub ecx,[ebp+__io_fetchbase]\n");
  if(cputype >= 68010) {
    emit("push ecx\n");/* old PC */
    emit("mov ecx,edx\n");
  }
  emit("mov edx,[ebp+__a7]\n");
  if(cputype >= 68010) {
    emit("and ecx,0FFCh\n");/* Format code */
    emit("sub edx,byte 2\n");
    emit("call writememoryword\n");
    emit("pop ecx\n");
  }
  emit("sub edx,byte 4\n");
  emit("call writememorydword\n");
  emit("pop ecx\n");/* old SR */
  emit("sub edx,byte 2\n");
  emit("call writememoryword\n");
  emit("mov [ebp+__a7],edx\n");
  emit("pop esi\n");/* dest. PC */
  emit("ret\n");
}

/***************************************************************************/

/* Privilege violation */
static void gen_privilege_violation(void){
  emit("privilege_violation:\n");
  emit("sub esi,byte 2\n");
  emit("mov edx,20h\n");
  emit("call group_1_exception\n");
  perform_cached_rebase();
  ret_timing((cputype==68010)?38:34);
}

/***************************************************************************/

static void usereg(void) {
  emit("and ebx,byte 7\n");
}

/* usereg only where applicable */
static void selective_usereg(void) {
  switch(main_eamode) {
  case dreg: case areg:
  case aind: case ainc: case adec:
  case adsp: case axdp:
    usereg();
  default:
    ;
  }
}

static void selftest(int size) {
  emit("test %s,%s\n", x86cx[size], x86cx[size]);
}

/***************************************************************************/

/* Get condition: Less Than (N^V)
** If true, the x86 sign flag will be set */
static void getcondition_l_s_ns(void) {
  emit("push eax\n");
  emit("neg al\n");
  emit("xor al,ah\n");
  emit("pop eax\n");
}

/* Get condition: Less Than or Equal ((N^V)|Z)
** If true, the x86 sign flag will be set */
static void getcondition_le_s_ns(void) {
  emit("push eax\n");
  emit("neg al\n");
  emit("xor al,ah\n");
  emit("add ah,ah\n");
  emit("or al,ah\n");
  emit("pop eax\n");
}

static char optcc[5];
static char optrc[5];

static void getcondition(int cc) {
  switch(cc) {
  case 0x0:
  case 0x1:
    break;
  case 0x2:/* a */
    emit("test ah,41h\n");
    sprintf(optcc, "z");
    sprintf(optrc, "nz");
    break;
  case 0x3:/* be */
    emit("test ah,41h\n");
    sprintf(optcc, "nz");
    sprintf(optrc, "z");
    break;
  case 0x4:/* nc */
    emit("test ah,1\n");
    sprintf(optcc, "z");
    sprintf(optrc, "nz");
    break;
  case 0x5:/* c */
    emit("test ah,1\n");
    sprintf(optcc, "nz");
    sprintf(optrc, "z");
    break;
  case 0x6:/* ne */
    emit("test ah,40h\n");
    sprintf(optcc, "z");
    sprintf(optrc, "nz");
    break;
  case 0x7:/* e */
    emit("test ah,40h\n");
    sprintf(optcc, "nz");
    sprintf(optrc, "z");
    break;
  case 0x8:/* no */
    emit("test al,1\n");
    sprintf(optcc, "z");
    sprintf(optrc, "nz");
    break;
  case 0x9:/* o */
    emit("test al,1\n");
    sprintf(optcc, "nz");
    sprintf(optrc, "z");
    break;
  case 0xA:/* ns */
    emit("or ah,ah\n");
    sprintf(optcc, "ns");
    sprintf(optrc, "s");
    break;
  case 0xB:/* s */
    emit("or ah,ah\n");
    sprintf(optcc, "s");
    sprintf(optrc, "ns");
    break;
  case 0xC:/* ge */
    getcondition_l_s_ns();
    sprintf(optcc, "ns");
    sprintf(optrc, "s");
    break;
  case 0xD:/* l */
    getcondition_l_s_ns();
    sprintf(optcc, "s");
    sprintf(optrc, "ns");
    break;
  case 0xE:/* g */
    getcondition_le_s_ns();
    sprintf(optcc, "ns");
    sprintf(optrc, "s");
    break;
  case 0xF:/* le */
    getcondition_le_s_ns();
    sprintf(optcc, "s");
    sprintf(optrc, "ns");
    break;
  default:break;
  }
}

static void flags(void) {
  emit("lahf\n");
  emit("seto al\n");
}

static void flags_v0(void) {
  emit("lahf\n");
  emit("mov al,0\n");
}

/* Put one of the x86 flags into the 68K zero flag. */
static void flag_to_z(char *f) {
  int myline = linenum; linenum += 2;
  emit("j%s short ln%d\n", f, myline);
  emit("and ah,0BFh\n");
  emit("jmp short ln%d\n", myline + 1);
  emit("ln%d:\n", myline);
  emit("or ah,40h\n");
  emit("ln%d:\n", myline + 1);
}

/* carry to X flag */
static void c2x(void) {
  emit("setc [ebp+__xflag]\n");
}

/* with previous flags in another register, adjust for non-changing zero */
static void adjzero(char *reg) {
  int myline = linenum; linenum++;
  emit("jnz short ln%d\n", myline);
  emit("or %s,0BFh\n", reg);
  emit("and ah,%s\n", reg);
  emit("ln%d:\n", myline);
}

/* Check for privilege violation */
static void privilegecheck(void) {
  emit("test byte[ebp+__sr+1],20h\n");
  emit("jz near privilege_violation\n");
}

/****************************************************************************
** EFFECTIVE ADDRESS GENERATION
****************************************************************************/

/*
** There are five types of EA activity:
**
** 1. Read:       precalc -> read -> postcalc
** 2. Write:      precalc -> write -> postcalc
** 3. R-M-W:      precalc -> read -> (modify) -> write -> postcalc
** 4. Move:       Read followed by Write
** 5. Control:    precalc
*/

/* Calculate address */
static void ea_step_precalc(int size, enum eamode mode, int reg) {
  char regs[100];
  if(reg == -1) sprintf(regs, "ebx*4");
  else sprintf(regs, "%d", reg * 4);
  switch(mode) {
  case dreg: case areg:
    break;
  case aind: case ainc: case adec:
    emit("mov edx,[ebp+__areg+%s]\n",regs);
    if(mode == adec) {
      /* Compensate for byte-sized stack ops */
      if(size == 1) {
        if(reg == -1) {
          emit("cmp bl,7\n");
          emit("adc edx,byte -2\n");
        } else if(reg == 7) {
          emit("sub edx,byte 2\n");
        } else {
          emit("dec edx\n");
        }
      } else {
        emit("sub edx,byte %d\n", size);
      }
    }
    break;
  case adsp:
    emit("movsx edx,word[esi]\n");
    emit("add esi,byte 2\n");
    emit("add edx,[ebp+__areg+%s]\n", regs);
    break;
  case axdp:
    emit("call decode_ext\n");
    emit("add edx,[ebp+__areg+%s]\n", regs);
    break;
  case absw:
    emit("movsx edx,word[esi]\n");
    emit("add esi,byte 2\n");
    break;
  case absl:
    emit("mov edx,dword[esi]\n");
    emit("add esi,byte 4\n");
    emit("rol edx,16\n");
    break;
  case pcdp:
    emit("movsx edx,word[esi]\n");
    emit("add edx,esi\n");
    emit("sub edx,[ebp+__io_fetchbase]\n");
    emit("add esi,byte 2\n");
    break;
  case pcxd:
    emit("call decode_ext\n");
    emit("add edx,esi\n");
    emit("sub edx,[ebp+__io_fetchbase]\n");
    emit("sub edx,byte 2\n");
    break;
  case immd:
    break;
  default:
    emit("#error ea_step_precalc\n");
    break;
  }
}

static void ea_step_read(int size, enum eamode mode, int reg) {
  char regs[100];
  if(reg == -1) sprintf(regs, "ebx*4");
  else sprintf(regs, "%d", reg * 4);
  switch(mode) {
  case dreg: emit("mov ecx,[ebp+__dreg+%s]\n", regs); break;
  case areg: emit("mov ecx,[ebp+__areg+%s]\n", regs); break;
  case aind: case ainc: case adec:
  case adsp: case axdp:
  case absw: case absl:
  case pcdp: case pcxd:
    emit("call readmemory%s\n", sizename[size]);
    break;
  case immd:
    switch(size) {
    case 1:
    case 2:
      emit("mov cx,[esi]\n");
      emit("add esi,byte 2\n");
      break;
    case 4:
      emit("mov ecx,[esi]\n");
      emit("add esi,byte 4\n");
      emit("rol ecx,16\n");
      break;
    default:
      emit("#error ea_step_read\n");
      break;
    }
    break;
  default:
    emit("#error ea_step_read\n");
    break;
  }
}

/*
** Special case for when you need to load a word and sign-extend it.
** This cuts some fat out of a few instructions (i.e. MOVEA).
*/
static void ea_step_read_signword(enum eamode mode, int reg) {
  char regs[100];
  if(reg == -1) sprintf(regs, "ebx*4");
  else sprintf(regs, "%d", reg * 4);
  switch(mode) {
  case dreg: emit("movsx ecx,word[ebp+__dreg+%s]\n", regs); break;
  case areg: emit("movsx ecx,word[ebp+__areg+%s]\n", regs); break;
  case aind: case ainc: case adec:
  case adsp: case axdp:
  case absw: case absl:
  case pcdp: case pcxd:
    emit("call readmemory%s\n", sizename[2]);
    emit("movsx ecx,cx\n");
    break;
  case immd:
    emit("movsx ecx,word[esi]\n");
    emit("add esi,byte 2\n");
    break;
  default:
    emit("#error ea_step_read_signword\n");
    break;
  }
}

static void ea_step_write(int size, enum eamode mode, int reg) {
  char regs[100];
  if(reg == -1) sprintf(regs, "ebx*4");
  else sprintf(regs, "%d", reg * 4);
  switch(mode) {
  case dreg:
    emit("mov [ebp+__dreg+%s],%s\n", regs, x86cx[size]);
    break;
  case aind: case ainc: case adec:
  case adsp: case axdp:
  case absw: case absl:
    emit("call writememory%s\n", sizename[size]);
    break;
  default:
    emit("#error ea_step_write\n");
    break;
  }
}

static void ea_step_postcalc(int size, enum eamode mode, int reg) {
  char regs[100];
  if(reg == -1) sprintf(regs, "ebx*4");
  else sprintf(regs, "%d", reg * 4);
  switch(mode) {
  case ainc:
    /* Compensate for byte-sized stack ops */
    if(size == 1) {
      if(reg == -1) {
        emit("cmp bl,7\n");
        emit("sbb edx,byte -2\n");
      } else if(reg == 7) {
        emit("add edx,byte 2\n");
      } else {
        emit("inc edx\n");
      }
    } else {
      emit("add edx,byte %d\n", size);
    }
    /* Fall through */
  case adec:
    /* Store already-predecremented address */
    emit("mov [ebp+__areg+%s],edx\n", regs);
    break;
  case dreg: case areg:
  case aind: case adsp: case axdp:
  case absw: case absl:
  case pcdp: case pcxd:
  case immd:
    break;
  default:
    emit("#error ea_step_postcalc\n");
    break;
  }
}

/* Combined EA routines */

static void ea_load(int size, enum eamode mode, int reg) {
  ea_step_precalc (size, mode, reg);
  ea_step_read    (size, mode, reg);
  ea_step_postcalc(size, mode, reg);
}

static void ea_load_signword(enum eamode mode, int reg) {
  ea_step_precalc      (2, mode, reg);
  ea_step_read_signword(   mode, reg);
  ea_step_postcalc     (2, mode, reg);
}

static void ea_store(int size, enum eamode mode, int reg) {
  ea_step_precalc (size, mode, reg);
  ea_step_write   (size, mode, reg);
  ea_step_postcalc(size, mode, reg);
}

static void ea_rmw_load(int size, enum eamode mode, int reg) {
  ea_step_precalc (size, mode, reg);
  ea_step_read    (size, mode, reg);
}

static void ea_rmw_store(int size, enum eamode mode, int reg) {
  ea_step_write   (size, mode, reg);
  ea_step_postcalc(size, mode, reg);
}

static void ea_control(enum eamode mode, int reg) {
  ea_step_precalc (0, mode, reg);
}

static void main_ea_load(void) {
  ea_load(main_size, main_eamode, -1);
}

static void main_ea_load_signed(void) {
  if(main_size < 4) {
    ea_load_signword(main_eamode, -1);
  } else {
    ea_load(main_size, main_eamode, -1);
  }
}

static void main_ea_store(void) {
  ea_store(main_size, main_eamode, -1);
}

static void main_ea_rmw_load(void) {
  ea_rmw_load(main_size, main_eamode, -1);
}

static void main_ea_rmw_store(void) {
  ea_rmw_store(main_size, main_eamode, -1);
}

static void main_ea_control(void) {
  ea_control(main_eamode, -1);
}

/***************************************************************************/
/*
** Calculate cycles for main EA mode
** (68000, 68010)
*/
static int main_ea_cycles(void) {
  int l;
  if(main_size == 4) l = 4; else l = 0;
  switch(main_eamode) {
  case aind: return(l +  4);
  case ainc: return(l +  4);
  case adec: return(l +  6);
  case adsp: return(l +  8);
  case axdp: return(l + 10);
  case absw: return(l +  8);
  case absl: return(l + 12);
  case pcdp: return(l +  8);
  case pcxd: return(l + 10);
  case immd: return(l +  4);
  default:   break;
  }
  return 0;
}

/* Calculate cycles for main EA mode, without fetching (68010 only) */
static int main_ea_cycles_nofetch(void){
  switch(main_eamode) {
  case aind: return(2);
  case ainc: return(4);
  case adec: return(4);
  case adsp: return(4);
  case axdp: return(8);
  case absw: return(4);
  case absl: return(8);
  default:   break;
  }
  return 0;
}

/****************************************************************************
** PREFIXES / SUFFIXES
****************************************************************************/

/*
** Prefixes - stuff that appears before the instruction handling routines
*/
static void prefixes(void) {
  /* Basic stuff - banner, variable section, API */
  gen_banner();
  gen_variables();
  gen_interface();
  /* Internal functions - PC rebasing, I/O, etc. */
  gen_basefunction();
  gen_decode_ext();
  gen_readbw(1);
  gen_readbw(2);
  gen_readl();
  gen_writebw(1);
  gen_writebw(2);
  gen_writel();
  gen_group_12_exception();
  gen_privilege_violation();
  gen_flush_interrupts();
}

/*
** Suffixes - stuff that appears after the instruction handling routines and
** the jump table / loop info table
*/
static void suffixes(void) {
  emit("end\n");
}

/****************************************************************************
** INSTRUCTION HANDLING ROUTINES
****************************************************************************/

/* called 600 times (!) */
static void i_move(void) {
  int cycles;
  selective_usereg();
  main_ea_load();
  ea_store(main_size, main_destmode, main_reg);
  selftest(main_size);
  flags_v0();
  cycles = 4 + main_ea_cycles();
  switch(main_destmode) {
  case aind: if(main_size == 4) cycles += 4; cycles +=  4; break;
  case ainc: if(main_size == 4) cycles += 4; cycles +=  4; break;
  case adec: if(main_size == 4) cycles += 4; cycles +=  4; break;
  case adsp: if(main_size == 4) cycles += 4; cycles +=  8; break;
  case axdp: if(main_size == 4) cycles += 4; cycles += 10; break;
  case absw: if(main_size == 4) cycles += 4; cycles +=  8; break;
  case absl: if(main_size == 4) cycles += 4; cycles += 12; break;
  default:   break;
  }
  /* Calculate loop mode timings */
  if(cputype == 68010) {
    switch(main_eamode) {
    case dreg: case areg:
      switch(main_destmode) {
      case aind: case ainc:
        if(main_size <= 2) {
          loop_c_cycles = 2;
          loop_t_cycles = 10;
          loop_x_cycles = 8;
        }else{
          loop_c_cycles = 2;
          loop_t_cycles = 8;
          loop_x_cycles = 6;
        }
        break;
      default:break;
      }
      break;
    case aind: case ainc: case adec:
      switch(main_destmode) {
      case aind: case ainc:
        loop_c_cycles = 2;
        loop_t_cycles = 8;
        loop_x_cycles = 6;
        break;
      case adec:
        loop_c_cycles = 4;
        loop_t_cycles = 10;
        loop_x_cycles = 8;
        break;
      default:break;
      }
      break;
    default:break;
    }
  }
  ret_timing(cycles);
}

static void i_moveq(void) {
  emit("movsx ecx,bl\n");
  emit("mov [ebp+__dreg+%d],ecx\n", main_reg * 4);
  selftest(1);
  flags_v0();
  /* No loop mode */
  ret_timing(4);
}

static void op_to_areg(char *s) {
  selective_usereg();
  main_ea_load_signed(); /* sign extends to ecx where necessary */
  emit("%s [ebp+__areg+%d],ecx\n", s, main_reg * 4);
}

static void i_movea(void) {
  op_to_areg("mov");
  /* No loop mode */
  ret_timing(4 + main_ea_cycles());
}

/* ADDA or SUBA */
static void addsuba(char *op) {
  int base_cycles;
  op_to_areg(op);
  if(main_size==4){
    base_cycles=6;
    /* Register direct / immediate penalty (68000) */
    if(cputype==68000){
      switch(main_eamode){
      case areg:case dreg:case immd:
        base_cycles+=2;
        break;
      default:break;
      }
    }
  }else{
    base_cycles=8;
  }
  /* Calculate loop mode timings */
  if(cputype==68010){
    switch(main_eamode){
    case aind:case ainc:case adec:
      if(main_size<=2){
        loop_c_cycles = 6;
        loop_t_cycles = 12;
        loop_x_cycles = 10;
      }else{
        loop_c_cycles = 8;
        loop_t_cycles = 14;
        loop_x_cycles = 12;
      }
      break;
    default:break;
    }
  }
  ret_timing(base_cycles+main_ea_cycles());
}

static void i_adda(void){addsuba("add");}
static void i_suba(void){addsuba("sub");}

static void i_cmpa(void){
  op_to_areg("cmp");
  flags();
  /* Calculate loop mode timings */
  if(cputype==68010){
    switch(main_eamode){
    case aind:case ainc:case adec:
      if(main_size<=2){
        loop_c_cycles = 2;
        loop_t_cycles = 8;
        loop_x_cycles = 6;
      }else{
        loop_c_cycles = 4;
        loop_t_cycles = 10;
        loop_x_cycles = 6;
      }
      break;
    default:break;
    }
  }
  ret_timing(6+main_ea_cycles());
}

static void i_move_to_sr(void){
  main_size=2;
  privilegecheck();
  selective_usereg();
  main_ea_load();
  cx2sr();
  /* No loop mode; check PPL and trace flag */
  ret_timing_checkpoint(12+main_ea_cycles());
}

static void i_move_to_ccr(void){
  main_size=2;/* WEIRD! But it works! */
  selective_usereg();
  main_ea_load();
  cl2ccr();
  /* No loop mode */
  ret_timing(12+main_ea_cycles());
}

static void i_move_from_sr(void){
  int cycles;
  /* This is privileged on 68010 and up */
  if(cputype>=68010)privilegecheck();
  main_size=2;
  selective_usereg();
  sr2cx();
  main_ea_store();
  if(cputype==68010){
    cycles=8+main_ea_cycles_nofetch();
  }else{
    cycles=8+main_ea_cycles();
  }
  if((main_eamode==dreg)||(main_eamode==areg)){
    cycles-=2;
    /* Speed demon 68010 can do it in 2 fewer cycles... :p */
    if(cputype==68010)cycles-=2;
  }
  /* No loop mode */
  ret_timing(cycles);
}

/* 68000/68008 aren't supposed to have this */
static void i_move_from_ccr(void){
  int cycles;
  main_size=2;
  selective_usereg();
  ccr2cl();
  main_ea_store();
  if(cputype==68010){
    cycles=8+main_ea_cycles_nofetch();
  }else{
    cycles=8+main_ea_cycles();
  }
  if((main_eamode==dreg)||(main_eamode==areg)){
    cycles-=2;
    if(cputype==68010)cycles-=2;
  }
  /* No loop mode */
  ret_timing(cycles);
}

static void op_to_ccr(char*op){
  ccr2cl();
  emit("%s cl,[esi]\n",op);
  emit("add esi,byte 2\n");
  cl2ccr();
  /* No loop mode */
  ret_timing((cputype==68010)?16:20);
}

static void op_to_sr(char*op){
  privilegecheck();
  sr2cx();
  emit("%s cx,[esi]\n",op);
  emit("add esi,byte 2\n");
  cx2sr();
  /* No loop mode */
  /* Check PPL and trace flag */
  ret_timing_checkpoint((cputype==68010)?16:20);
}

static void  i_ori_ccr(void){op_to_ccr("or" );}
static void i_andi_ccr(void){op_to_ccr("and");}
static void i_eori_ccr(void){op_to_ccr("xor");}
static void  i_ori_sr (void){op_to_sr ("or" );}
static void i_andi_sr (void){op_to_sr ("and");}
static void i_eori_sr (void){op_to_sr ("xor");}

static void i_clr(void){
  int cycles=0;
  selective_usereg();
  emit("xor ecx,ecx\n");
  main_ea_store();
  if(cputype==68000){
    cycles=main_ea_cycles();
    if((main_eamode==dreg)||(main_eamode==areg)){
      cycles+=4;if(main_size==4)cycles+=4;
    }else{
      cycles+=6;if(main_size==4)cycles+=6;
    }
  }else if(cputype==68010){
    switch(main_eamode){
    case dreg:cycles= 4;break;
    case aind:cycles= 8;break;
    case ainc:cycles= 8;break;
    case adec:cycles=10;break;
    case adsp:cycles=12;break;
    case axdp:cycles=16;break;
    case absw:cycles=12;break;
    case absl:cycles=16;break;
    default:break;
    }
    if(main_size==4){
      cycles+=4;
      if(main_eamode==dreg)cycles-=2;
    }
    /* Calculate loop mode timings */
    switch(main_eamode){
    case aind:case ainc:case adec:
      loop_c_cycles = 2;
      loop_t_cycles = 10;
      loop_x_cycles = 8;
      break;
    default:break;
    }
  }
  emit("mov ax,4000h\n");
  ret_timing(cycles);
}

static void i_tst(void){
  selective_usereg();
  main_ea_load();
  /* Calculate loop mode timings */
  if(cputype==68010){
    switch(main_eamode){
    case aind:case ainc:case adec:
      if(main_size<=2){
        loop_c_cycles = 4;
        loop_t_cycles = 10;
        loop_x_cycles = 8;
      }else{
        loop_c_cycles = 6;
        loop_t_cycles = 12;
        loop_x_cycles = 8;
      }
      break;
    default:break;
    }
  }
  selftest(main_size);
  flags_v0();
  ret_timing(4+main_ea_cycles());
}

/* Always affects X flag
** (except for #,An which affects no flags whatsoever) */
static void op_quick(char*op){
  int cycles;
  selective_usereg();
  if(main_eamode==dreg){
    emit(
      "%s %s[ebp+__dreg+ebx*4],byte %d\n",
      op,sizename[main_size],quickvalue[main_qv]
    );
    flags();
    c2x();
    cycles=4;
  }else if(main_eamode==areg){
    emit(
      "%s dword[ebp+__areg+ebx*4],byte %d\n",
      op,quickvalue[main_qv]
    );
    cycles=4;
    /* SUBQ.W #,An incurs 4-cycle penalty (68000 only) */
    if(cputype==68000){
      if((main_size==2)&&(op[0]=='s'))cycles+=4;
    }
  }else{
    main_ea_rmw_load();
    emit(
      "%s %s,byte %d\n",op,x86cx[main_size],quickvalue[main_qv]
    );
    flags();
    c2x();
    main_ea_rmw_store();
    cycles=8+main_ea_cycles();
  }
  if(main_size==4)cycles+=4;
  /* No loop mode */
  ret_timing(cycles);
}

static void i_addq(void){op_quick("add");}
static void i_subq(void){op_quick("sub");}

static void op_to_dn(char*op,int affectx,int logical){
  int cycles;
  selective_usereg();
  main_ea_load();
  emit("%s [ebp+__dreg+%d],%s\n",
    op,main_reg*4,x86cx[main_size]
  );
  if(logical){
    flags_v0();
  }else{
    flags();
  }
  if(affectx)c2x();
  cycles=4+main_ea_cycles();
  if(main_size==4){
    cycles+=2;
    /* Register direct / immediate penalty (68000) */
    if((cputype==68000)&&(op[0]!='c')){
      switch(main_eamode){
      case areg:case dreg:case immd:
        cycles+=2;
        break;
      default:break;
      }
    }
  }
  /* Calculate loop mode timings */
  if(cputype==68010){
    switch(main_eamode){
    case aind:case ainc:case adec:
      switch(op[0]){
      case 'a':/* ADD, AND */
      case 'o':/* OR */
        loop_c_cycles = 8;
        loop_t_cycles = 14;
        loop_x_cycles = 12;
        break;
      case 's':/* SUB */
        if(main_size<=2){
          loop_c_cycles = 8;
          loop_t_cycles = 14;
          loop_x_cycles = 12;
        }else{
          loop_c_cycles = 6;
          loop_t_cycles = 12;
          loop_x_cycles = 10;
        }
        break;
      case 'c':/* CMP */
        loop_c_cycles = 4;
        loop_t_cycles = 10;
        loop_x_cycles = 8;
        if(main_size==4)loop_x_cycles = 6;
        break;
      default:break;
      }
      break;
    default:break;
    }
  }
  ret_timing(cycles);
}

static void i_cmp_dn(void){op_to_dn("cmp",0,0);}
static void i_add_dn(void){op_to_dn("add",1,0);}
static void i_sub_dn(void){op_to_dn("sub",1,0);}
static void i_and_dn(void){op_to_dn("and",0,1);}
static void i_or_dn (void){op_to_dn("or" ,0,1);}

static void op_to_ea(char*op,int logical){
  int cycles;
  selective_usereg();
  main_ea_rmw_load();
  emit(
    "%s %s,[ebp+__dreg+%d]\n",
    op,x86cx[main_size],main_reg*4
  );
  if(logical){
    flags_v0();
  }else{
    flags();
  }
  /* Logical instructions don't affect X flag */
  if(!logical)c2x();
  main_ea_rmw_store();
  cycles=8+main_ea_cycles();
  if(main_size==4)cycles+=4;
  /* EOR Dn,Dn takes fewer cycles than we'd expect */
  if((op[0]=='x')&&(main_eamode==dreg)){
    cycles-=4;
    if(cputype==68010)cycles-=2;
  }
  /* Calculate loop mode timings */
  if(cputype==68010){
    switch(main_eamode){
    case aind:case ainc:case adec:
      loop_c_cycles = 4;
      loop_t_cycles = 10;
      loop_x_cycles = 8;
      break;
    default:break;
    }
  }
  ret_timing(cycles);
}

static void i_eor_ea(void){op_to_ea("xor",1);}
static void i_add_ea(void){op_to_ea("add",0);}
static void i_sub_ea(void){op_to_ea("sub",0);}
static void i_and_ea(void){op_to_ea("and",1);}
static void i_or_ea (void){op_to_ea("or" ,1);}

/* called 144 times */
/*
** c1: Total cycles for #,Dn (byte/word)
** c2: Total cycles for #,Dn (long)
** c3: Basic cycles for #,M  (byte/word)
** c4: Basic cycles for #,M  (long)
*/
static void im_to_ea(char*op,int wback,int affectx,int logical,
  int c1,int c2,int c3,int c4){
  int cycles;
  selective_usereg();
  switch(main_size){
  case 1:
  case 2:
    emit("mov cx,[esi]\n");
    emit("add esi,byte 2\n");
    break;
  case 4:
    emit("mov ecx,[esi]\n");
    emit("rol ecx,16\n");
    emit("add esi,byte 4\n");
    break;
  default:break;
  }
  if(main_eamode==dreg){
    emit("%s [ebp+__dreg+ebx*4],%s\n",
      op,x86cx[main_size]
    );
    if(logical){
      flags_v0();
    }else{
      flags();
    }
    if(affectx)c2x();
    if(main_size<4)cycles=c1;else cycles=c2;
  }else{
    emit("push ecx\n");
    if(wback)main_ea_rmw_load();else main_ea_load();
    emit("%s %s,[esp]\n",op,x86cx[main_size]);
    if(logical){
      flags_v0();
    }else{
      flags();
    }
    if(affectx)c2x();
    emit("add esp,byte 4\n");
    if(wback)main_ea_rmw_store();
    if(main_size<4)cycles=c3;else cycles=c4;
    cycles+=main_ea_cycles();
  }
  /* No loop mode */
  ret_timing(cycles);
}

/* each called 24 times */
static void i_addi(void){im_to_ea("add",1,1,0,8,(cputype==68010)?14:16,12,20);}
static void i_subi(void){im_to_ea("sub",1,1,0,8,(cputype==68010)?14:16,12,20);}
static void i_cmpi(void){im_to_ea("cmp",0,0,0,8,(cputype==68010)?12:14, 8,12);}
static void i_andi(void){im_to_ea("and",1,0,1,8,(cputype==68010)?14:14,12,20);}
static void i_ori (void){im_to_ea("or" ,1,0,1,8,(cputype==68010)?14:16,12,20);}
static void i_eori(void){im_to_ea("xor",1,0,1,8,(cputype==68010)?14:16,12,20);}

static void flick_reg(char*op,int needxf,int affectx,int asl,int rotate){
  int cycles;
  char tmps[5];
  int i;
  usereg();
  cycles=6;
  if(main_size==4)cycles+=2;
  /* ASR doesn't need overflow checking */
  if(direction[main_dr]=='r')asl=0;
  if(main_ir==1){
    int myline=linenum;linenum++;
    emit("mov ecx,[ebp+__dreg+%d]\n",main_reg*4);
    emit("and ecx,byte 63\n");
    emit("jnz short ln%d\n",myline);
    /* The shift count was zero.  Strange things are about to
    ** happen... */
    ea_load(main_size,dreg,-1);/* get data in eax */
    selftest(main_size);
    flags_v0();
    if(needxf){/* ROXL/ROXR: Set C flag equal to X */
      emit("and ah,0FEh\n");
      emit("or ah,[ebp+__xflag]\n");
    }
    ret_timing(cycles);
    /* Shift count non-zero */
    emit("ln%d:\n",myline);
    emit("sub edi,ecx\n");
    emit("sub edi,ecx\n");
    sprintf(tmps,"cl");
  }else{
    sprintf(tmps,"%d",quickvalue[main_reg]);
    cycles+=2*quickvalue[main_reg];
  }
  if(asl){
    switch(tmps[0]){
    case 'c':/* register shift count */
      emit("mov edx,[ebp+__dreg+ebx*4]\n");
      emit("mov al,0\n");/* overflow starts at 0 */
      emit("ln%d:\n",linenum);
      emit("add %s,%s\n",
        x86dx[main_size],x86dx[main_size]
      );
      emit("lahf\n");/* grab N,Z,C flags */
      emit("seto ch\n");
      emit("or al,ch\n");/* add overflow */
      emit("dec cl\n");
      emit("jnz short ln%d\n",linenum);linenum++;
      emit("mov [ebp+__dreg+ebx*4],%s\n",x86dx[main_size]);
      if(affectx){
        emit("mov cl,ah\n");
        emit("and cl,1\n");
        emit("mov [ebp+__xflag],cl\n");
      }
      break;
    case '1':/* immediate shift count ==1 */
      emit("sal %s[ebp+__dreg+ebx*4],1\n",
        sizename[main_size]
      );
      flags();
      if(affectx)c2x();
      break;
    default:/* immediate shift count >1 */
      emit("mov edx,[ebp+__dreg+ebx*4]\n");
      emit("mov al,0\n");/* overflow starts at 0 */
      for(i='1';i<=tmps[0];i++){
        emit("add %s,%s\n",
          x86dx[main_size],x86dx[main_size]
        );
        if(i==tmps[0]){
          /* grab N,Z,C flags */
          emit("lahf\n");
        }
        emit("seto ch\n");
        emit("or al,ch\n");/* add overflow */
      }
      emit("mov [ebp+__dreg+ebx*4],%s\n",x86dx[main_size]);
      if(affectx){
        emit("mov cl,ah\n");
        emit("and cl,1\n");
        emit("mov [ebp+__xflag],cl\n");
      }
      break;
    }
  }else{
    if(rotate){
      emit("mov edx,[ebp+__dreg+ebx*4]\n");
      if(needxf){
        emit("mov al,[ebp+__xflag]\n");
      }else{
        emit("mov al,0\n");
      }

/******** Stef Fix (Gens) *********/

  switch(tmps[0])
  {
    case 'c':/* register shift count */
      emit("cmp cl, 32\n");
      emit("jb short ln%d\n",linenum);
      emit("sub cl, 31\n");
      if(needxf){
        emit("shr al, 1\n");
      }
      emit("%s%c %s, 31\n", op,direction[main_dr],x86dx[main_size]);
      emit("%s%c %s,%s\n", op,direction[main_dr],x86dx[main_size],tmps);
      emit("jmp short ln%d\n",linenum + 1);
      emit("ln%d:\n",linenum); linenum++;
      if(needxf){
        emit("shr al, 1\n");
      }
      emit("%s%c %s,%s\n", op,direction[main_dr],x86dx[main_size],tmps);
      emit("ln%d:\n",linenum); linenum++;
      break;

    default:/* immediate shift count >1 */
      if(needxf){
        emit("shr al,1\n");
      }
      emit("%s%c %s,%s\n", op,direction[main_dr],x86dx[main_size],tmps);
      break;
  }

/********     End Fix     *********/

      emit("adc al,al\n");
      emit("or %s,%s\n",
        x86dx[main_size],x86dx[main_size]
      );
      emit("lahf\n");
      emit("or ah,al\n");
      if(affectx){
        emit("mov [ebp+__xflag],al\n");
      }
      emit("mov al,0\n");
      emit("mov [ebp+__dreg+ebx*4],%s\n",x86dx[main_size]);
    }else{
      if(needxf){
        emit("mov al,[ebp+__xflag]\n");
      }else{
        emit("mov al,0\n");
      }

/******** Stef Fix (Gens) *********/

  switch(tmps[0])
  {
    case 'c':/* register shift count */
      emit("cmp cl, 32\n");
      emit("jb short ln%d\n",linenum);
      emit("sub cl, 31\n");
      if(needxf){
        emit("shr al, 1\n");
      }
      emit("%s%c %s[ebp+__dreg+ebx*4], 31\n", op,direction[main_dr],sizename[main_size]);
      emit("jmp short ln%d\n",linenum + 1);
      emit("ln%d:\n",linenum); linenum++;
      if(needxf){
        emit("shr al, 1\n");
      }
      emit("%s%c %s[ebp+__dreg+ebx*4],%s\n", op,direction[main_dr],sizename[main_size],tmps);
      emit("ln%d:\n",linenum); linenum++;
      break;

    default:/* immediate shift count >1 */
      if(needxf){
        emit("shr al, 1\n");
      }
      emit("%s%c %s[ebp+__dreg+ebx*4],%s\n", op,direction[main_dr],sizename[main_size],tmps);
      break;
  }

/********     End Fix     *********/

      emit("lahf\n");
      if(affectx)c2x();
    }
  }
  /* No loop mode */
  ret_timing(cycles);
}

static void i_lsx_reg(void){flick_reg("sh",0,1,0,0);}
static void i_asx_reg(void){flick_reg("sa",0,1,1,0);}
static void i_rox_reg(void){flick_reg("ro",0,0,0,1);}
static void i_rxx_reg(void){flick_reg("rc",1,1,0,1);}

static void flick_mem(char*op,int needxf,int affectx,int vf,int rotate){
  /* ASR doesn't need overflow checking */
  if(direction[main_dr]=='r')vf=0;
  main_size=2;
  selective_usereg();
  main_ea_rmw_load();
  if(needxf){
    emit("mov al,[ebp+__xflag]\n");
    emit("shr al,1\n");
  }else{
    if(rotate)emit("mov al,0\n");
  }
  emit("%s%c cx,1\n",op,direction[main_dr]);
  if(rotate){
    emit("adc al,al\n");
    emit("test cx,cx\n");
    emit("lahf\n");
    emit("or ah,al\n");
    if(affectx)emit("mov [ebp+__xflag],al\n");
    emit("mov al,0\n");
  }else{
    if(vf){
      flags();
    }else{
      flags_v0();
    }
    if(affectx)c2x();
  }
  main_ea_rmw_store();
  /* Calculate loop mode timings */
  if(cputype==68010){
    switch(main_eamode){
    case aind:case ainc:case adec:
      loop_c_cycles = 6;
      loop_t_cycles = 12;
      loop_x_cycles = 10;
      break;
    default:break;
    }
  }
  ret_timing(8+main_ea_cycles());
}

static void i_lsx_mem(void){flick_mem("sh",0,1,0,0);}
static void i_asx_mem(void){flick_mem("sa",0,1,1,0);}
static void i_rox_mem(void){flick_mem("ro",0,0,0,1);}
static void i_rxx_mem(void){flick_mem("rc",1,1,0,1);}

static int created_bra_b=0;
static void i_bra_b(void){
  if(!created_bra_b){emit("r_bra_b:\n");created_bra_b=1;}
  emit("movsx ebx,bl\n");
  emit("add esi,ebx\n");
  emit("xor ebx,ebx\n");
  ret_timing(10);
}

static int created_bra_w=0;
static void i_bra_w(void){
  if(!created_bra_w){emit("r_bra_w:\n");created_bra_w=1;}
  emit("movsx ebx,word[esi]\n");
  emit("add esi,ebx\n");
  emit("xor ebx,ebx\n");
  ret_timing(10);
}

static void i_bsr_b(void){
  emit("movsx ebx,bl\n");
  emit("mov ecx,esi\n");
  emit("sub ecx,[ebp+__io_fetchbase]\n");
  emit("add esi,ebx\n");
  emit("xor ebx,ebx\n");
  ea_store(4,adec,7);
  ret_timing(18);
}

static void i_bsr_w(void){
  emit("movsx ebx,word[esi]\n");
  emit("mov ecx,esi\n");
  emit("add ecx,byte 2\n");
  emit("sub ecx,[ebp+__io_fetchbase]\n");
  emit("add esi,ebx\n");
  emit("xor ebx,ebx\n");
  ea_store(4,adec,7);
  ret_timing(18);
}

static void i_bcc_b(void){
  getcondition(main_cc);
  emit("j%s near r_bra_b\n",optcc);
  ret_timing((cputype==68010)?6:8);
}

static void i_bcc_w(void){
  getcondition(main_cc);
  emit("j%s near r_bra_w\n",optcc);
  emit("add esi,byte 2\n");/* skip relative offset */
  ret_timing(12);
}

/* called once */
static void i_dbra(void){
  emit("r_dbra:\n");
  usereg();
  if(cputype==68010){
    emit("movsx ecx,word[esi]\n");
    emit("cmp ecx,byte -4\n");
    emit("je short r_loopmode_dbra\n");

    /* Regular DBRA */
    emit("r_regular_dbra:\n");
    emit("sub word[ebp+__dreg+ebx*4],byte 1\n");
    emit("jc short r_dbra_expire\n");
    emit("add esi,ecx\n");
    ret_timing(10);
    emit("r_dbra_expire:\n");
    emit("add esi,byte 2\n");
    ret_timing(16);

    /* Loop mode DBRA */
    emit("r_loopmode_dbra:\n");
    emit("cmp byte[ebp+__loopmode],0\n");
    emit("jnz short r_loopmode_dbra_inloop\n");
    emit("mov byte[ebp+__loopmode],1\n");
    emit("jmp short r_regular_dbra\n");
    emit("r_loopmode_dbra_inloop:\n");
    emit("sub word[ebp+__dreg+ebx*4],byte 1\n");
    emit("jc short r_loopmode_dbra_expire\n");

    /* Continue */
    emit("mov bx,word[esi-4]\n");
    emit("add esi,ecx\n");
    /* Subtract continuation cycles */
    emit("mov cl,byte[_looptbl+ebx]\n");
    emit("and ecx,byte 0Eh\n");
    emit("sub edi,ecx\n");
    ret_timing(0);

    /* Expire */
    emit("r_loopmode_dbra_expire:\n");
    emit("mov bx,word[esi-4]\n");
    emit("mov byte[ebp+__loopmode],0\n");
    /* Subtract continuation and extra expiration cycles */
    emit("mov cl,byte[_looptbl+ebx]\n");
    emit("mov ebx,ecx\n");
    emit("rol cl,2\n");
    emit("add esi,byte 2\n");
    emit("and ebx,byte 0Eh\n");
    emit("and ecx,byte 06h\n");
    emit("sub edi,ebx\n");
    emit("sub edi,ecx\n");
    ret_timing(0);

    /* Terminate (visited externally by i_dbcc) */
    emit("r_dbra_terminate:\n");
    emit("cmp word[esi],byte -4\n");
    emit("jne short r_dbra_terminate_regular\n");
    emit("cmp byte[ebp+__loopmode],0\n");
    emit("je short r_dbra_terminate_regular\n");
    emit("mov bx,word[esi-4]\n");
    emit("mov byte[ebp+__loopmode],0\n");
    /* Subtract termination cycles */
    emit("mov cl,byte[_looptbl+ebx]\n");
    emit("shr cl,3\n");
    emit("add esi,byte 2\n");
    emit("and ecx,byte 0Eh\n");
    emit("sub edi,ecx\n");
    ret_timing(0);
    emit("r_dbra_terminate_regular:\n");
    ret_timing(10);
  }else{
    emit("sub word[ebp+__dreg+ebx*4],byte 1\n");
    emit("jnc near r_bra_w\n");
    emit("add esi,byte 2\n");
    ret_timing(14);
  }
}

static void i_dbcc(void){
  getcondition(main_cc);
  emit("j%s near r_dbra\n",optrc);
  /* Terminate */
  if(cputype==68010){
    emit("jmp r_dbra_terminate\n");
  }else{
    emit("add esi,byte 2\n");
    ret_timing(12);
  }
}

static void i_scc(void){
  int cycles;
  main_size=1;
  selective_usereg();
  if(main_cc>1){
    if((main_eamode==dreg)||(main_eamode==areg)){
      if(cputype==68000){
        emit("xor ecx,ecx\n");
      }
      getcondition(main_cc);
      emit("set%s cl\n",optcc);
      if(cputype==68000){
        /* 2 extra cycles if it's true */
        emit("sub edi,ecx\n");
        emit("sub edi,ecx\n");
      }
      emit("neg cl\n");
      cycles=4;
    }else{
      getcondition(main_cc);
      emit("set%s cl\n",optcc);
      emit("neg cl\n");
      cycles=8;
    }
  }else{
    emit("mov cl,%d\n",(main_cc^1)*0xFF);
    if((main_eamode==dreg)||(main_eamode==areg)){
      cycles=4;
      if(cputype==68000){
        /* 2 extra cycles if it's true */
        if(!main_cc)cycles+=2;
      }
    }else{
      cycles=8;
    }
  }
  main_ea_store();
  if(cputype==68010){
    cycles+=main_ea_cycles_nofetch();
  }else{
    cycles+=main_ea_cycles();
  }
  /* No loop mode */
  ret_timing(cycles);
}

/* bit operations */

/* called 315 times */
/* 0=btst,1=bchg,2=bclr,3=bset */
static void bitop(int static_cycles){
  int cycles;
  selective_usereg();
  emit("and ecx,byte %d\n",(main_eamode==dreg)?31:7);
  if(main_eamode==dreg){
    main_size=4;
    if(!main_cc){/* BTST */
      emit("mov edx,1\n");
      emit("shl edx,cl\n");
      emit("test [ebp+__dreg+ebx*4],edx\n");
      flag_to_z("z");
    }else{
      emit("mov edx,1\n");
      emit("shl edx,cl\n");
      emit("mov ecx,[ebp+__dreg+ebx*4]\n");
      emit("test ecx,edx\n");
      flag_to_z("z");
      switch(main_cc){
      case 1:emit("xor ecx,edx\n");break;
      case 2:emit("not edx\nand ecx,edx\n");break;
      case 3:emit("or ecx,edx\n");break;
      default:break;
      }
      emit("mov [ebp+__dreg+ebx*4],ecx\n");
    }
    cycles=6+static_cycles;
    if(main_cc)cycles+=2;
    if(main_cc==2)cycles+=2;
  }else{
    main_size=1;
    if(!main_cc){
      emit("push ecx\n");
      main_ea_load();
      emit("mov edx,ecx\n");
      emit("pop ecx\n");
      emit("inc cl\n");
      emit("shr dl,cl\n");
      flag_to_z("nc");
    }else{
      emit("mov dl,1\n");
      emit("shl dl,cl\n");
      emit("push edx\n");
      main_ea_rmw_load();
      emit("xchg edx,[esp]\n");
      emit("test cl,dl\n");
      flag_to_z("z");
      switch(main_cc){
      case 1:emit("xor cl,dl\n");break;
      case 2:emit("not dl\nand cl,dl\n");break;
      case 3:emit("or cl,dl\n");break;
      default:break;
      }
      emit("pop edx\n");
      main_ea_rmw_store();
    }
    cycles=4+static_cycles+main_ea_cycles();
    if(main_cc)cycles+=4;
    if((cputype==68010)&&(main_cc==2))cycles+=2;
  }
  ret_timing(cycles);
}

/* called 35 times */
static void i_bitop_imm(void){
  emit("mov cl,[esi]\n");
  emit("add esi,byte 2\n");
  bitop(4);
}

/* called 280 times */
static void i_bitop_reg(void){
  emit("mov cl,byte[ebp+__dreg+%d]\n",main_reg*4);
  bitop(0);
}

static void i_jmp(void){
  int cycles=0;
  selective_usereg();
  main_ea_control();
  emit("mov esi,edx\n");
  perform_cached_rebase();
  switch(main_eamode){
  case aind:cycles= 8;break;
  case adsp:cycles=10;break;
  case axdp:cycles=14;break;
  case absw:cycles=10;break;
  case absl:cycles=12;break;
  case pcdp:cycles=10;break;
  case pcxd:cycles=14;break;
  default:break;
  }
  /* No loop mode */
  ret_timing(cycles);
}

static void i_jsr(void){
  int cycles=0;
  selective_usereg();
  main_ea_control();
  emit("mov ecx,esi\n");
  emit("sub ecx,[ebp+__io_fetchbase]\n");
  emit("mov esi,edx\n");
  perform_cached_rebase();
  ea_store(4,adec,7);
  switch(main_eamode){
  case aind:cycles=16;break;
  case adsp:cycles=18;break;
  case axdp:cycles=22;break;
  case absw:cycles=18;break;
  case absl:cycles=20;break;
  case pcdp:cycles=18;break;
  case pcxd:cycles=22;break;
  default:break;
  }
  /* No loop mode */
  ret_timing(cycles);
}

static void i_rts(void){
  ea_load(4,ainc,7);
  emit("mov esi,ecx\n");
  perform_cached_rebase();
  /* No loop mode */
  ret_timing(16);
}

/* 68010 and higher */
static void i_rtd(void){
  emit("mov edx,[ebp+__a7]\n");
  emit("call readmemorydword\n");
  emit("movsx ebx,word[esi]\n");
  emit("add edx,ebx\n");
  emit("mov esi,ecx\n");
  emit("add edx,byte 4\n");
  emit("xor ebx,ebx\n");
  emit("mov [ebp+__a7],edx\n");
  perform_cached_rebase();
  /* No loop mode */
  ret_timing(16);
}

static void i_rtr(void){
  ea_load(2,ainc,7);
  cl2ccr();
  ea_load(4,ainc,7);
  emit("mov esi,ecx\n");
  perform_cached_rebase();
  /* No loop mode */
  ret_timing(20);
}

static void i_rte(void){
  int myline=linenum;
  linenum++;
  privilegecheck();
  if(cputype>=68010){
    /* Check stack frame format - must be 0xxx or 8xxx */
    emit("mov edx,[ebp+__a7]\n");
    emit("add edx,byte 6\n");
    emit("call readmemory%s\n",sizename[2]);
    emit("test ch,70h\n");
    emit("jnz short ln%d_formatok\n",myline);
    /* Generate Format Error exception where necessary */
    emit("mov edx,38h\n");
    emit("call group_1_exception\n");
    perform_cached_rebase();
    ret_timing(50);/* RTE, Illegal Format */
    emit("ln%d_formatok:\n",myline);

    /* Now _we_ check to make sure the format isn't 8xxx (since
    ** that's not implemented yet). */
    emit("or ch,ch\n");
    emit("jns short ln%d_formatok2\n",myline);
    /* Double fault with error code 80000002h. */
    emit("mov ecx,80000002h\n");
    emit("or byte[ebp+__pc],1\n");
    emit("or byte[ebp+__interrupts],1\n");
    emit("jmp execexit\n");
    emit("ln%d_formatok2:\n",myline);

    /* Now RTE as usual */
    emit("sub edx,byte 6\n");
    emit("call readmemory%s\n",sizename[2]);
    emit("add edx,byte 2\n");
    cx2sr();
    emit("test ch,20h\n");
    emit("jz short ln%d_nosupe\n",myline);
    emit("add dword [ebp+__a7],byte 8\n");
    emit("jmp short ln%d_finish\n",myline);
    emit("ln%d_nosupe:\n",myline);
    emit("add dword [ebp+__asp],byte 8\n");
    emit("ln%d_finish:\n",myline);
    emit("call readmemory%s\n",sizename[4]);
    emit("mov esi,ecx\n");
    perform_cached_rebase();
    ret_timing_checkpoint(24);/* RTE with no trouble */
  }else{
    emit("mov edx,[ebp+__a7]\n");
    emit("call readmemory%s\n",sizename[2]);
    emit("add edx,byte 2\n");
    cx2sr();
    emit("test ch,20h\n");
    emit("jz short ln%d_nosupe\n",myline);
    emit("add dword [ebp+__a7],byte 6\n");
    emit("jmp short ln%d_finish\n",myline);
    emit("ln%d_nosupe:\n",myline);
    emit("add dword [ebp+__asp],byte 6\n");
    emit("ln%d_finish:\n",myline);
    emit("call readmemory%s\n",sizename[4]);
    emit("mov esi,ecx\n");
    perform_cached_rebase();
    ret_timing_checkpoint(20);
  }
}

static void i_lea(void){
  int cycles=0;
  selective_usereg();
  main_ea_control();
  emit("mov [ebp+__areg+%d],edx\n",main_reg*4);
  switch(main_eamode){
  case aind:cycles= 4;break;
  case adsp:cycles= 8;break;
  case axdp:cycles=12;break;
  case absw:cycles= 8;break;
  case absl:cycles=12;break;
  case pcdp:cycles= 8;break;
  case pcxd:cycles=12;break;
  default:break;
  }
  /* No loop mode */
  ret_timing(cycles);
}

static void i_pea(void){
  int cycles=0;
  selective_usereg();
  main_ea_control();
  emit("mov ecx,edx\n");
  ea_store(4,adec,7);
  switch(main_eamode){
  case aind:cycles=12;break;
  case adsp:cycles=16;break;
  case axdp:cycles=20;break;
  case absw:cycles=16;break;
  case absl:cycles=20;break;
  case pcdp:cycles=16;break;
  case pcxd:cycles=20;break;
  default:break;
  }
  /* No loop mode */
  ret_timing(cycles);
}

static void i_nop(void){
  /* No loop mode */
  ret_timing(4);
}

static void i_movem_control(void){
  int cycles=0;
  int myline=linenum;linenum+=2;
  emit("push eax\n");
  selective_usereg();
  emit("mov ax,[esi]\n");
  emit("add esi,byte 2\n");
  main_ea_control();
  emit("xor ebx,ebx\n");
  emit("ln%d:\n",myline);
  emit("shr eax,1\n");
  emit("jnc short ln%d\n",myline+1);
  if(main_dr==0){/*register->memory*/
    switch(main_eamode){
    case aind:cycles= 8;break;
    case adsp:cycles=12;break;
    case axdp:cycles=14;break;
    case absw:cycles=12;break;
    case absl:cycles=16;break;
    default:break;
    }
    emit("mov ecx,[ebp+__reg+ebx]\n");
    emit("call writememory%s\n",sizename[main_size]);
  }else{/*memory->register*/
    switch(main_eamode){
    case aind:cycles=12;
      if((cputype==68010)&&(main_size==4))cycles=24;
      break;
    case adsp:cycles=16;break;
    case axdp:cycles=18;break;
    case absw:cycles=16;break;
    case absl:cycles=20;break;
    case pcdp:cycles=16;break;
    case pcxd:cycles=18;break;
    default:break;
    }
    emit("call readmemory%s\n",sizename[main_size]);
    if(main_size==2)emit("movsx ecx,cx\n");
    emit("mov [ebp+__reg+ebx],ecx\n");
  }
  emit("add edx,byte %d\n",main_size);
  emit("sub edi,byte %d\n",main_size*2);
  emit("ln%d:\n",myline+1);
  emit("add ebx,byte 4\n");
  emit("cmp ebx,byte 64\n");
  emit("jne short ln%d\n",myline);
  emit("pop eax\n");
  ret_timing(cycles);
}

static void i_movem_postinc(void){
  int myline=linenum;linenum+=2;
  usereg();
  emit("push eax\n");
  emit("mov ax,[esi]\n");
  emit("add esi,byte 2\n");
  emit("mov edx,[ebp+__areg+ebx*4]\n");
  emit("push ebx\n");
  emit("xor ebx,ebx\n");
  emit("ln%d:\n",myline);
  emit("shr eax,1\n");
  emit("jnc short ln%d\n",myline+1);
  emit("call readmemory%s\n",sizename[main_size]);
  if(main_size==2)emit("movsx ecx,cx\n");
  emit("mov [ebp+__reg+ebx],ecx\n");
  emit("add edx,byte %d\n",main_size);
  emit("sub edi,byte %d\n",main_size*2);
  emit("ln%d:\n",myline+1);
  emit("add ebx,byte 4\n");
  emit("cmp ebx,byte 64\n");
  emit("jne short ln%d\n",myline);
  emit("pop ebx\n");
  emit("pop eax\n");
  emit("mov [ebp+__areg+ebx*4],edx\n");
  ret_timing(12);
}

static void i_movem_predec(void){
  int myline=linenum;linenum+=2;
  usereg();
  emit("push eax\n");
  emit("mov ax,[esi]\n");
  emit("add esi,byte 2\n");
  emit("mov edx,[ebp+__areg+ebx*4]\n");
  emit("push ebx\n");
  emit("mov ebx,60\n");
  emit("ln%d:\n",myline);
  emit("shr eax,1\n");
  emit("jnc short ln%d\n",myline+1);
  emit("mov ecx,[ebp+__reg+ebx]\n");
  emit("sub edx,byte %d\n",main_size);
  emit("sub edi,byte %d\n",main_size*2);
  emit("call writememory%s\n",sizename[main_size]);
  emit("ln%d:\n",myline+1);
  emit("sub ebx,byte 4\n");
  emit("jns short ln%d\n",myline);
  emit("pop ebx\n");
  emit("pop eax\n");
  emit("mov [ebp+__areg+ebx*4],edx\n");
  ret_timing(8);
}

static void i_link(void){
  usereg();
  emit("mov ecx,[ebp+__areg+ebx*4]\n");
  ea_store(4,adec,7);
  emit("mov ecx,[ebp+__a7]\n");
  emit("mov [ebp+__areg+ebx*4],ecx\n");
  emit("movsx edx,word[esi]\n");
  emit("add ecx,edx\n");
  emit("mov [ebp+__a7],ecx\n");
  emit("add esi,byte 2\n");
  ret_timing(16);
}

static void i_unlk(void){
  usereg();
  emit("mov ecx,[ebp+__areg+ebx*4]\n");
  emit("mov [ebp+__a7],ecx\n");
  ea_load(4,ainc,7);
  emit("mov [ebp+__areg+ebx*4],ecx\n");
  ret_timing(12);
}

static void i_move_from_usp(void){
  privilegecheck(); /* makes our job much easier... */
  usereg();
  emit("mov ecx,[ebp+__asp]\n");
  emit("mov [ebp+__areg+ebx*4],ecx\n");
  ret_timing((cputype==68010)?6:4);
}

static void i_move_to_usp(void){
  privilegecheck();
  usereg();
  emit("mov ecx,[ebp+__areg+ebx*4]\n");
  emit("mov [ebp+__asp],ecx\n");
  ret_timing((cputype==68010)?6:4);
}

static void i_trap(void){
  emit("and ebx,byte 0Fh\n");
  emit("lea edx,[80h+ebx*4]\n");
  emit("call group_2_exception\n");
  perform_cached_rebase();
  ret_timing((cputype==68010)?38:34);
}

static void i_trapv(void){
  int myline=linenum;linenum++;
  emit("test al,1\n");
  emit("jnz short ln%d\n",myline);
  ret_timing(4);
  emit("ln%d:\n",myline);
  emit("mov edx,1Ch\n");
  emit("call group_2_exception\n");
  perform_cached_rebase();
  ret_timing(4+((cputype==68010)?38:34));
}

static void i_stop(void){
  int myline=linenum;linenum++;
  privilegecheck();
  emit("mov cx,[esi]\n");
  emit("add esi,2\n");
  cx2sr();
  emit("or byte[ebp+__interrupts],1\n");
  /* Forfeit all remaining cycles */
  emit("sub edi,byte 4\n");
  emit("js short ln%d\n",myline);
  emit("xor edi,edi\n");
  emit("dec edi\n");
  emit("ln%d:\n",myline);
  ret_timing(0);
}

static void i_extbw(void){
  usereg();
  emit("movsx cx,byte[ebp+__dreg+ebx*4]\n");
  emit("mov [ebp+__dreg+ebx*4],cx\n");
  selftest(2);
  flags_v0();
  ret_timing(4);
}

static void i_extwl(void){
  usereg();
  emit("movsx ecx,word[ebp+__dreg+ebx*4]\n");
  emit("mov [ebp+__dreg+ebx*4],ecx\n");
  selftest(4);
  flags_v0();
  ret_timing(4);
}

static void i_swap(void){
  usereg();
  emit("mov ecx,[ebp+__dreg+ebx*4]\n");
  emit("rol ecx,16\n");
  emit("mov [ebp+__dreg+ebx*4],ecx\n");
  selftest(4);
  flags_v0();
  ret_timing(4);
}

/* if main_cc==1 then it's signed */
static void i_mul(void){
  int base_cycles;
  selective_usereg();
  main_size=2;
  main_ea_load();
  emit("mov eax,ecx\n");
  /* Finally!  Real MULS/MULU timing! */
  if(cputype==68000){
    emit("mov dl,0\n");
    emit("mov bl,16\n");
    emit("ln%d:\n",linenum);
    emit("add cx,cx\n");
    if(main_cc==1){
      /* MULS: count the number of 10 or 01 pairs */
      emit("seto dh\n");
      emit("add dl,dh\n");
    }else{
      /* MULU: count the number of 1s */
      emit("adc dl,0\n");
    }
    emit("dec bl\n");
    emit("jnz ln%d\n",linenum);linenum++;
    emit("and edx,byte 127\n");
    emit("sub edi,edx\n");
    emit("sub edi,edx\n");
  }
  emit("%smul word[ebp+__dreg+%d]\n",
    (main_cc==1)?"i":"",main_reg*4
  );
  emit("shl edx,16\n");
  emit("and eax,0FFFFh\n");
  emit("mov ecx,edx\n");
  emit("or ecx,eax\n");
  flags_v0();
  emit("mov [ebp+__dreg+%d],ecx\n",main_reg*4);
  if(cputype==68010){
    /* Maximum is 42 signed, 40 unsigned */
    base_cycles=36;
    if(main_cc)base_cycles+=2;
  }else{
    /* 38+2n, signed or unsigned, maximum is 70 */
    base_cycles=38;
  }
  /* No loop mode */
  ret_timing(base_cycles+main_ea_cycles());
}

/* if main_cc=1 then it's signed */
static void i_div(void){
  int base_cycles;
  int myline=linenum;linenum+=2;
  selective_usereg();
  main_size=2;
  main_ea_load();
  emit("test cx,cx\n");
  emit("jnz short ln%d\n",myline);
  /* Forgot to put on our Division by Zero Suit... */
  emit("mov edx,14h\n");
  emit("call group_2_exception\n");
  perform_cached_rebase();
  base_cycles=38;
  if(cputype==68010)base_cycles+=4;
  ret_timing(base_cycles+main_ea_cycles());
  emit("ln%d:\n",myline);myline++;
  if(main_cc){
    emit("movsx ecx,cx\n");
  }else{
    emit("and ecx,0FFFFh\n");
  }
  emit("mov eax,[ebp+__dreg+%d]\n",main_reg*4);
  if(main_cc){
    emit("mov edx,eax\n");
    emit("sar edx,31\n");
  }else{
    emit("xor edx,edx\n");
  }
  emit("%sdiv ecx\n",main_cc?"i":"");
  if(main_cc){
    emit("mov ecx,eax\n");
    emit("sar cx,15\n");
    emit("or ecx,ecx\n");
    emit("je short ln%d\n",linenum);
    emit("inc ecx\n");
    emit("jne short ln%d\n",myline);
    emit("ln%d:\n",linenum);linenum++;
    emit("and eax,0FFFFh\n");
  }else{
    emit("test eax,0FFFF0000h\n");
    emit("jnz short ln%d\n",myline);
  }
  emit("shl edx,16\n");
  emit("mov dx,ax\n");
  emit("test dx,dx\n");
  flags_v0();
  emit("mov [ebp+__dreg+%d],edx\n",main_reg*4);
  if(cputype==68010){
    base_cycles=108;
    if(main_cc)base_cycles=122;
  }else{
    /* Varies from 142-158 signed, 126-140 unsigned */
    base_cycles=133;
    if(main_cc)base_cycles=150;
  }
  ret_timing(base_cycles+main_ea_cycles());
  /* Overflow */
  emit("ln%d:\n",myline);myline++;
  emit("mov ax,1\n");
  /* No loop mode */
  ret_timing(base_cycles+main_ea_cycles());
}

static void i_neg(void){
  int cycles;
  selective_usereg();
  cycles=4;
  if(main_size==4)cycles=6;
  if(main_eamode==dreg){
    emit("neg %s[ebp+__dreg+ebx*4]\n",
      sizename[main_size]
    );
    flags();
    c2x();
  }else{
    cycles*=2;
    main_ea_rmw_load();
    emit("neg %s\n",x86cx[main_size]);
    flags();
    c2x();
    main_ea_rmw_store();
    cycles+=main_ea_cycles();
  }
  /* Calculate loop mode timings */
  if(cputype==68010){
    switch(main_eamode){
    case aind:case ainc:case adec:
      loop_c_cycles = 4;
      loop_t_cycles = 10;
      loop_x_cycles = 8;
      break;
    default:break;
    }
  }
  ret_timing(cycles);
}

static void i_negx(void){
  int cycles;
  selective_usereg();
  cycles=4;
  if(main_size==4)cycles=6;
  if(main_eamode==dreg){
    emit("mov cl,[ebp+__xflag]\n");
    emit("shr cl,1\n");
    if(main_size==1)emit("mov cl,0\n");
    else emit("mov ecx,0\n");
    emit("sbb %s,[ebp+__dreg+ebx*4]\n",x86cx[main_size]);
    emit("mov edx,eax\n");
    flags();c2x();
    adjzero("dh");
    emit("mov [ebp+__dreg+ebx*4],%s\n",x86cx[main_size]);
  }else{
    cycles*=2;
    main_ea_rmw_load();
    emit("push ebx\n");
    emit("mov bl,[ebp+__xflag]\n");
    emit("shr bl,1\n");
    if(main_size==1)emit("mov bl,0\n");
    else emit("mov ebx,0\n");
    emit("sbb %s,%s\n",x86bx[main_size],x86cx[main_size]);
    emit("mov ecx,ebx\n");
    emit("mov ebx,eax\n");
    flags();c2x();
    adjzero("bh");
    emit("pop ebx\n");
    main_ea_rmw_store();
    cycles+=main_ea_cycles();
  }
  /* Calculate loop mode timings */
  if(cputype==68010){
    switch(main_eamode){
    case aind:case ainc:case adec:
      loop_c_cycles = 4;
      loop_t_cycles = 10;
      loop_x_cycles = 8;
      break;
    default:break;
    }
  }
  ret_timing(cycles);
}

static void i_nbcd(void){
  int cycles;
  main_size=1;
  selective_usereg();
  main_ea_rmw_load();
  /* Get the X flag into carry */
  emit("mov cl,[ebp+__xflag]\n");
  emit("shr cl,1\n");
  /* Save the previous Z flag in CH */
  emit("mov ch,ah\n");
  /* Perform the BCD subtraction */
  emit("mov al,0\n");
  emit("sbb al,cl\n");
  emit("das\n");
  /* Save result in CL */
  emit("mov cl,al\n");
  /* Set flags - V undefined */
  flags_v0();
  c2x();
  /* Adjust for non-changing Z (previous Z flag in CH) */
  adjzero("ch");
  main_ea_rmw_store();
  if(main_eamode==dreg){
    cycles=6;
  }else{
    cycles=8+main_ea_cycles();
  }
  /* Calculate loop mode timings */
  if(cputype==68010){
    switch(main_eamode){
    case aind:case ainc:case adec:
      loop_c_cycles = 6;
      loop_t_cycles = 12;
      loop_x_cycles = 10;
      break;
    default:break;
    }
  }
  ret_timing(cycles);
}

static void i_tas(void){
  int cycles;
  main_size=1;
  selective_usereg();
  main_ea_rmw_load();
  selftest(1);
  flags_v0();
  emit("or cl,80h\n");
  main_ea_rmw_store();
  if((main_eamode==dreg)||(main_eamode==areg)){
    cycles=4;
  }else{
    cycles=14+main_ea_cycles();
  }
  /* No loop mode */
  ret_timing(cycles);
}

static void i_not(void){
  int cycles;
  selective_usereg();
  cycles=4;
  if(main_size==4)cycles=6;
  if(main_eamode==dreg){
    emit("xor %s[ebp+__dreg+ebx*4],byte -1\n",
      sizename[main_size]
    );
    flags_v0();
  }else{
    cycles*=2;
    main_ea_rmw_load();
    emit("xor %s,byte -1\n",x86cx[main_size]);
    flags_v0();
    main_ea_rmw_store();
    cycles+=main_ea_cycles();
  }
  /* Calculate loop mode timings */
  if(cputype==68010){
    switch(main_eamode){
    case aind:case ainc:case adec:
      loop_c_cycles = 4;
      loop_t_cycles = 10;
      loop_x_cycles = 8;
      break;
    default:break;
    }
  }
  ret_timing(cycles);
}

/* main_reg=rx, main_dr is 0 or 32 (rx a/d), main_ir is 0 or 32 (ry a/d) */
static void i_exg(void){
  usereg();
  emit("mov ecx,[ebp+__reg+%d]\n",(main_reg*4)+main_dr);
  emit("mov edx,[ebp+__reg+%d+ebx*4]\n",main_ir);
  emit("mov [ebp+__reg+%d],edx\n",(main_reg*4)+main_dr);
  emit("mov [ebp+__reg+%d+ebx*4],ecx\n",main_ir);
  /* No loop mode */
  ret_timing(6);
}

static void i_cmpm(void){
  usereg();
  ea_load(main_size,ainc,-1);/* Keep this in order */
  emit("mov eax,ecx\n");
  ea_load(main_size,ainc,main_reg);
  emit("cmp %s,%s\n",x86cx[main_size],x86ax[main_size]);
  flags();
  /* Calculate loop mode timings */
  if(cputype==68010){
    if(main_size<=2){
      loop_c_cycles = 2;
      loop_t_cycles = 8;
      loop_x_cycles = 6;
    }else{
      loop_c_cycles = 4;
      loop_t_cycles = 10;
      loop_x_cycles = 6;
    }
  }
  ret_timing((main_size==4)?20:12);
}

static void opx_dreg(char*op,char*adjust){
  int cycles;
  usereg();
  emit("mov ch,ah\n");/* Save old Z flag in CH */
  emit("mov cl,[ebp+__xflag]\n");
  emit("shr cl,1\n");/* X -> x86 carry */
  emit("mov eax,[ebp+__dreg+%d]\n",main_reg*4);
  emit("%s %s,[ebp+__dreg+ebx*4]\n",op,x86ax[main_size]);
  if(adjust[0]){
    emit("%s\n",adjust);
  }
  emit("mov [ebp+__dreg+%d],%s\n",main_reg*4,x86ax[main_size]);
  emit("lahf\n");
  if(adjust[0]){
    emit("mov al,0\n");
  }else{
    emit("seto al\n");
  }
  c2x();
  adjzero("ch");
  if(main_size<=2){
    cycles=4;
    if(adjust[0])cycles=6;
  }else{
    cycles=8;
    if(cputype==68010)cycles=6;
  }
  /* No loop mode */
  ret_timing(cycles);
}

static void opx_adec(char*op,char*adjust){
  int cycles;
  usereg();
  ea_load(main_size,adec,-1);/* Keep this in order */
  emit("mov ebx,ecx\n");
  ea_rmw_load(main_size,adec,main_reg);
  emit("xchg ecx,eax\n");/* flags -> ECX, dest -> EAX */
  emit("mov cl,[ebp+__xflag]\n");
  emit("shr cl,1\n");/* X -> x86 carry */
  emit("%s %s,%s\n",op,x86ax[main_size],x86bx[main_size]);
  if(adjust[0]){
    emit("%s\n",adjust);
  }
  emit("mov ebx,eax\n");
  emit("lahf\n");
  if(adjust[0]){
    emit("mov al,0\n");
  }else{
    emit("seto al\n");
  }
  c2x();
  adjzero("ch");
  emit("mov ecx,ebx\n");
  emit("xor ebx,ebx\n");
  ea_rmw_store(main_size,adec,main_reg);
  if(main_size<=2){
    cycles=18;
  }else{
    cycles=30;
  }
  /* Calculate loop mode timings */
  if(cputype==68010){
    if(adjust[0]){
      loop_c_cycles = 6;
      loop_t_cycles = 12;
      loop_x_cycles = 10;
    }else{
      if(main_size<=2){
        loop_c_cycles = 4;
        loop_t_cycles = 10;
        loop_x_cycles = 8;
      }else{
        loop_c_cycles = 2;
        loop_t_cycles = 8;
        loop_x_cycles = 6;
      }
    }
  }
  ret_timing(cycles);
}

static void i_addx_dreg(void){opx_dreg("adc","");}
static void i_addx_adec(void){opx_adec("adc","");}
static void i_subx_dreg(void){opx_dreg("sbb","");}
static void i_subx_adec(void){opx_adec("sbb","");}
static void i_abcd_dreg(void){main_size=1;opx_dreg("adc","daa");}
static void i_abcd_adec(void){main_size=1;opx_adec("adc","daa");}
static void i_sbcd_dreg(void){main_size=1;opx_dreg("sbb","das");}
static void i_sbcd_adec(void){main_size=1;opx_adec("sbb","das");}

static void i_movep_mem2reg(void){
  int cycles;
  usereg();
  emit("movsx edx,word[esi]\n");
  emit("add esi,byte 2\n");
  emit("add edx,[ebp+__areg+ebx*4]\n");
  emit("call readmemorybyte\n");
  emit("mov bh,cl\n");
  emit("add edx,byte 2\n");
  emit("call readmemorybyte\n");
  emit("mov bl,cl\n");
  if(main_size==2){
    emit("mov [ebp+__dreg+%d],bx\n",main_reg*4);
    cycles=16;
  }else{
    emit("add edx,byte 2\n");
    emit("shl ebx,16\n");
    emit("call readmemorybyte\n");
    emit("mov bh,cl\n");
    emit("add edx,byte 2\n");
    emit("call readmemorybyte\n");
    emit("mov bl,cl\n");
    emit("mov [ebp+__dreg+%d],ebx\n",main_reg*4);
    emit("xor ebx,ebx\n");
    cycles=24;
  }
  /* No loop mode */
  ret_timing(cycles);
}

static void i_movep_reg2mem(void){
  int cycles;
  usereg();
  emit("movsx edx,word[esi]\n");
  emit("add esi,byte 2\n");
  emit("add edx,[ebp+__areg+ebx*4]\n");
  emit("mov ebx,[ebp+__dreg+%d]\n",main_reg*4);
  if(main_size==4)emit("rol ebx,16\n");
  emit("mov cl,bh\n");
  emit("call writememorybyte\n");
  emit("add edx,byte 2\n");
  emit("mov cl,bl\n");
  emit("call writememorybyte\n");
  if(main_size==4){
    emit("add edx,byte 2\n");
    emit("rol ebx,16\n");
    emit("mov cl,bh\n");
    emit("call writememorybyte\n");
    emit("add edx,byte 2\n");
    emit("mov cl,bl\n");
    emit("call writememorybyte\n");
    cycles=24;
  }else{
    cycles=16;
  }
  emit("xor ebx,ebx\n");
  /* No loop mode */
  ret_timing(cycles);
}

static void i_chk(void){
  int cycles;
  int myline=linenum;linenum++;
  selective_usereg();
  main_ea_load();
  emit("cmp %s[ebp+__dreg+%d],byte 0\n",
    sizename[main_size],main_reg*4
  );
  emit("mov ax,8000h\n");
  emit("jl short ln%d\n",myline);
  emit("cmp [ebp+__dreg+%d],%s\n",
    main_reg*4,x86cx[main_size]
  );
  emit("mov ax,0\n");
  emit("jg short ln%d\n",myline);
  cycles=10;
  if(cputype==68010)cycles=8;
  ret_timing(cycles+main_ea_cycles());
  /* Out of bounds, so generate CHK exception */
  emit("ln%d:",myline);
  emit("mov edx,18h\n");
  emit("call group_2_exception\n");
  perform_cached_rebase();
  cycles=40;
  if(cputype==68010)cycles=44;
  /* No loop mode */
  ret_timing(cycles+main_ea_cycles());
}

static int created_illegal=0;
static void i_illegal(void){
  if(!created_illegal){emit("r_illegal:\n");created_illegal=1;}
  emit("sub esi,byte 2\n");
  emit("mov edx,10h\n");
  emit("call group_1_exception\n");
  perform_cached_rebase();
  ret_timing((cputype==68010)?38:34);
}

/* Breakpoint - notify hardware */
static void i_bkpt(void){
  int myline=linenum;
  linenum++;
  emit("mov edx,[ebp+__bkpthandler]\n");
  emit("or edx,edx\n");
  emit("jz ln%d\n",myline);
  airlock_exit();
  if(use_stack) {
    emit("push dword[ebp+__hwstate]\n");
  } else {
    emit("mov ecx,[ebp+__hwstate]\n");
  }
  emit("call edx\n");
  if(use_stack) {
    emit("add esp,byte 4\n");
  }
  airlock_enter();
  emit("ln%d:\n",myline);
  emit("mov edx,10h\n");
  emit("call group_1_exception\n");
  perform_cached_rebase();
  ret_timing((cputype==68010)?38:34);
}

static void i_aline(void){
  emit("sub esi,byte 2\n");
  emit("mov edx,28h\n");
  emit("call group_1_exception\n");
  perform_cached_rebase();
  /* This is just a guess */
  ret_timing((cputype==68010)?38:34);
}

static void i_fline(void){
  emit("sub esi,byte 2\n");
  emit("mov edx,2Ch\n");
  emit("call group_1_exception\n");
  perform_cached_rebase();
  /* This is just a guess */
  ret_timing((cputype==68010)?38:34);
}

static void i_reset(void){
  privilegecheck();
  emit("mov edx,[ebp+__resethandler]\n");
  emit("or edx,edx\n");
  emit("jz near invalidins\n");
  airlock_exit();
  if(use_stack) {
    emit("push dword[ebp+__hwstate]\n");
  } else {
    emit("mov ecx,[ebp+__hwstate]\n");
  }
  emit("call edx\n");
  if(use_stack) {
    emit("add esp,byte 4\n");
  }
  airlock_enter();
  ret_timing((cputype==68010)?130:132);
}

static void i_movec_c_to_r(void){
  int myline=linenum;linenum++;
  privilegecheck();
  emit("mov bx,word[esi]\n");
  emit("mov edx,ebx\n");
  emit("shr ebx,12\n");

  emit("and edx,0FFFh\n");
  emit("jnz short ln%d\n",linenum);
  emit("mov cl,[ebp+__sfc]\n");
  emit("and ecx,byte 7\n");
  emit("jmp short ln%d\n",myline);
  emit("ln%d:\n",linenum);linenum++;

  emit("cmp edx,byte 1\n");
  emit("jnz short ln%d\n",linenum);
  emit("mov cl,[ebp+__dfc]\n");
  emit("and ecx,byte 7\n");
  emit("jmp short ln%d\n",myline);
  emit("ln%d:\n",linenum);linenum++;

  emit("cmp edx,0800h\n");
  emit("jnz short ln%d\n",linenum);
  emit("mov ecx,[ebp+__asp]\n");
  emit("jmp short ln%d\n",myline);
  emit("ln%d:\n",linenum);linenum++;

  emit("cmp edx,0801h\n");
  emit("jnz short ln%d\n",linenum);
  emit("mov ecx,[ebp+__vbr]\n");
  emit("jmp short ln%d\n",myline);
  emit("ln%d:\n",linenum);linenum++;

  emit("jmp r_illegal\n");

  emit("ln%d:\n",myline);
  emit("add esi,byte 2\n");
  emit("mov dword[ebp+__reg+ebx*4],ecx\n");
  ret_timing(12);
}

static void i_movec_r_to_c(void){
  int myline=linenum;linenum++;
  privilegecheck();
  emit("mov bx,word[esi]\n");
  emit("mov edx,ebx\n");
  emit("shr ebx,12\n");
  emit("mov ecx,dword[ebp+__reg+ebx*4]\n");

  emit("and edx,0FFFh\n");
  emit("jnz short ln%d\n",linenum);
  emit("and cl,7\n");
  emit("mov [ebp+__sfc],cl\n");
  emit("jmp short ln%d\n",myline);
  emit("ln%d:\n",linenum);linenum++;

  emit("cmp edx,byte 1\n");
  emit("jnz short ln%d\n",linenum);
  emit("and cl,7\n");
  emit("mov [ebp+__dfc],cl\n");
  emit("jmp short ln%d\n",myline);
  emit("ln%d:\n",linenum);linenum++;

  emit("cmp edx,0800h\n");
  emit("jnz short ln%d\n",linenum);
  emit("mov [ebp+__asp],ecx\n");
  emit("jmp short ln%d\n",myline);
  emit("ln%d:\n",linenum);linenum++;

  emit("cmp edx,0801h\n");
  emit("jnz short ln%d\n",linenum);
  emit("mov [ebp+__vbr],ecx\n");
  emit("jmp short ln%d\n",myline);
  emit("ln%d:\n",linenum);linenum++;

  emit("jmp r_illegal\n");

  emit("ln%d:\n",myline);
  emit("add esi,byte 2\n");
  ret_timing(10);
}

#if 0
/* called not-quite-so-many times as i_move */
static void i_moves(void){
  int cycles;
  int unitsize = main_size == 4 ? 2 : main_size;
  int myline=linenum;
  linenum++;
  selective_usereg();
  emit("push esi\n"); /* save in case it's invalid */
  emit("movzx ecx,word[esi]\n");
  emit("add esi,byte 2\n");
  ea_step_precalc(main_size,main_eamode,-1); /* edx=address */
  emit("shr ecx,12\n");
  emit("jc short ln%d_write\n",myline);
  /* read */
  emit("cmp byte[ebp+__sfc],1\n");
  emit("je ln%d_read_userdata\n");
  emit("cmp byte[ebp+__sfc],2\n");
  emit("je ln%d_read_userprogram\n");
  emit("cmp byte[ebp+__sfc],5\n");
  emit("je ln%d_read_superdata\n");
  emit("cmp byte[ebp+__sfc],6\n");
  emit("je ln%d_read_superprogram\n");

  /*
  ** Generic address space read routine; SFC in {0,3,4,7}
  */
  emit("cmp dword[ebp+__fc_read%s],byte 0\n",sizename[unitsize]);
  emit("je short ln%d_inv\n",myline);
  emit("push ecx\n");
  emit("push edx\n");
  airlock_exit();
  emit("mov al,[ebp+__sfc]\n");
  emit("and eax,byte 7\n");
  if(use_stack) {
    emit("push edx\n");
    emit("push eax\n");
  }
  emit("call dword[ebp+__fc_read%s]\n");
  if(use_stack) {
    emit("add esp,byte 8\n");
  }
  emit("mov ecx,[esp+%d]\n",airlock_stacksize+4);
  switch(main_size) {
  case 1:emit("mov [ebp+__reg+ecx*4],al\n");break;
  case 2:emit("mov [ebp+__reg+ecx*4],ax\n");break;
  case 4:emit("mov [ebp+__reg+ecx*4+2],ax\n");
    emit("mov edx,[esp+%d]\n",airlock_stacksize);
    emit("add edx,byte 2\n");
    emit("mov al,[ebp+__sfc]\n");
    emit("and eax,byte 7\n");
    if(use_stack) {
      emit("push edx\n");
      emit("push eax\n");
    }
    emit("call dword[ebp+__fc_read%s]\n");
    if(use_stack) {
      emit("add esp,byte 8\n");
    }
    emit("mov ecx,[esp+%d]\n",airlock_stacksize+4);
    emit("mov [ebp+__reg+ecx*4],ax\n");
    break;
  }
  airlock_enter();
  emit("pop edx\n");
  emit("pop ecx\n");
  ea_step_postcalc(main_size,main_eamode,-1); /* edx=address */
  emit("jmp ln%d_end\n");

  ret_timing(cycles);
}
#endif

/****************************************************************************
** DECODE ROUTINES
****************************************************************************/

static int  rproc [0x10000];
static byte unique[0x10000];
static int  cease_decode;

static int test(int n, int m, int op) {
  int t;
  if((n & m) != op) return 0;
  for(t = op & 0xF000; t < n; t++) {
    if((!unique[t]) && ((t & m) == (n & m))) {
      rproc[n] = t;
      return 2;
    }
  }
  unique[n] = (m >> 16) & 1;
  rproc[n] = n;
  t = (m ^ 0xFFFF) & 0xFFF;
  if(!t) {
    emit("; Opcode %04X\n", n);
  } else {
    emit("; Opcodes %04X - %04X\n", n, op + t);
  }
  emit("%c%03X:\n", ((n >> 12) & 0xF) + 'K', n & 0xFFF);
  routine_counter++;
  return 1;
}

/* Instruction definition routine */
static void idef(
  int n, int mask, int op, void(*proc)(void)
) {
  if(cease_decode) return;
  cease_decode = test(n, mask, op);
  if(cease_decode == 1) {
    if(cputype == 68010) {
      loop_c_cycles = 10;
      loop_t_cycles = 10;
      loop_x_cycles = 16;
    }
    proc();
    if(cputype == 68010) {
      if(loop_c_cycles > 14) {
        fprintf(stderr,
          "Bad news: instruction %04X:\n"
          "loop_c_cycles (%d) exceeds limit\n",
          n, loop_c_cycles
        );
        exit(1);
      }
      if(loop_t_cycles > 14) {
        fprintf(stderr,
          "Bad news: instruction %04X:\n"
          "loop_t_cycles (%d) exceeds limit\n",
          n, loop_t_cycles
        );
        exit(1);
      }
      if(loop_x_cycles > (loop_c_cycles + 6)) {
        fprintf(stderr,
          "Bad news: instruction %04X:\n"
          "loop_c_cycles (%d) and "
          "loop_x_cycles (%d) too far apart\n",
          n, loop_c_cycles, loop_x_cycles
        );
        exit(1);
      }
      loop_x_cycles -= loop_c_cycles;
      loopinfo[n] =
        (((loop_c_cycles     ) & 0x0E)  |
         ((loop_t_cycles << 3) & 0x70)) |
        (((loop_x_cycles << 6) & 0x80)  |
         ((loop_x_cycles >> 2) & 0x01));
    }
  }
}

/* Batch idef for all addressing modes */
static void eadef_all(
  int n, int m, int op, void(*proc)(void)
) {
  if(cease_decode) return;
  main_eamode = dreg; idef(n, m | 0x38, op | 0x00, proc);
  main_eamode = areg; idef(n, m | 0x38, op | 0x08, proc);
  main_eamode = aind; idef(n, m | 0x38, op | 0x10, proc);
  main_eamode = ainc; idef(n, m | 0x38, op | 0x18, proc);
  main_eamode = adec; idef(n, m | 0x38, op | 0x20, proc);
  main_eamode = adsp; idef(n, m | 0x38, op | 0x28, proc);
  main_eamode = axdp; idef(n, m | 0x38, op | 0x30, proc);
  main_eamode = absw; idef(n, m | 0x3F, op | 0x38, proc);
  main_eamode = absl; idef(n, m | 0x3F, op | 0x39, proc);
  main_eamode = pcdp; idef(n, m | 0x3F, op | 0x3A, proc);
  main_eamode = pcxd; idef(n, m | 0x3F, op | 0x3B, proc);
  main_eamode = immd; idef(n, m | 0x3F, op | 0x3C, proc);
}

/* Batch idef for all addressing modes, excluding Address Register Direct
**  mode when the operand size is 1 */
static void eadef_all_nobyteaddress(
  int n, int m, int op, void(*proc)(void)
) {
  if(cease_decode) return;
  main_eamode = dreg; idef(n, m | 0x38, op | 0x00, proc);
  if(main_size != 1) {
    main_eamode = areg; idef(n, m | 0x38, op | 0x08, proc);
  }
  main_eamode = aind; idef(n, m | 0x38, op | 0x10, proc);
  main_eamode = ainc; idef(n, m | 0x38, op | 0x18, proc);
  main_eamode = adec; idef(n, m | 0x38, op | 0x20, proc);
  main_eamode = adsp; idef(n, m | 0x38, op | 0x28, proc);
  main_eamode = axdp; idef(n, m | 0x38, op | 0x30, proc);
  main_eamode = absw; idef(n, m | 0x3F, op | 0x38, proc);
  main_eamode = absl; idef(n, m | 0x3F, op | 0x39, proc);
  main_eamode = pcdp; idef(n, m | 0x3F, op | 0x3A, proc);
  main_eamode = pcxd; idef(n, m | 0x3F, op | 0x3B, proc);
  main_eamode = immd; idef(n, m | 0x3F, op | 0x3C, proc);
}

/* Batch idef for all data addressing modes */
static void eadef_data(
  int n, int m, int op, void(*proc)(void)
) {
  if(cease_decode) return;
  main_eamode = dreg; idef(n, m | 0x38, op | 0x00, proc);
  main_eamode = aind; idef(n, m | 0x38, op | 0x10, proc);
  main_eamode = ainc; idef(n, m | 0x38, op | 0x18, proc);
  main_eamode = adec; idef(n, m | 0x38, op | 0x20, proc);
  main_eamode = adsp; idef(n, m | 0x38, op | 0x28, proc);
  main_eamode = axdp; idef(n, m | 0x38, op | 0x30, proc);
  main_eamode = absw; idef(n, m | 0x3F, op | 0x38, proc);
  main_eamode = absl; idef(n, m | 0x3F, op | 0x39, proc);
  main_eamode = pcdp; idef(n, m | 0x3F, op | 0x3A, proc);
  main_eamode = pcxd; idef(n, m | 0x3F, op | 0x3B, proc);
  main_eamode = immd; idef(n, m | 0x3F, op | 0x3C, proc);
}

/* Batch idef for all alterable addressing modes, excluding Address Register
**  Direct mode when the operand size is 1 */
static void eadef_alterable_nobyteaddress(
  int n, int m, int op, void(*proc)(void)
) {
  if(cease_decode) return;
  main_eamode = dreg; idef(n, m | 0x38, op | 0x00, proc);
  if(main_size != 1) {
    main_eamode = areg; idef(n, m | 0x38, op | 0x08, proc);
  }
  main_eamode = aind; idef(n, m | 0x38, op | 0x10, proc);
  main_eamode = ainc; idef(n, m | 0x38, op | 0x18, proc);
  main_eamode = adec; idef(n, m | 0x38, op | 0x20, proc);
  main_eamode = adsp; idef(n, m | 0x38, op | 0x28, proc);
  main_eamode = axdp; idef(n, m | 0x38, op | 0x30, proc);
  main_eamode = absw; idef(n, m | 0x3F, op | 0x38, proc);
  main_eamode = absl; idef(n, m | 0x3F, op | 0x39, proc);
}

/* Batch idef for all data alterable addressing modes */
static void eadef_data_alterable(
  int n, int m, int op, void(*proc)(void)
) {
  if(cease_decode) return;
  main_eamode = dreg; idef(n, m | 0x38, op | 0x00, proc);
  main_eamode = aind; idef(n, m | 0x38, op | 0x10, proc);
  main_eamode = ainc; idef(n, m | 0x38, op | 0x18, proc);
  main_eamode = adec; idef(n, m | 0x38, op | 0x20, proc);
  main_eamode = adsp; idef(n, m | 0x38, op | 0x28, proc);
  main_eamode = axdp; idef(n, m | 0x38, op | 0x30, proc);
  main_eamode = absw; idef(n, m | 0x3F, op | 0x38, proc);
  main_eamode = absl; idef(n, m | 0x3F, op | 0x39, proc);
}

/* Batch idef for all memory alterable addressing modes */
static void eadef_memory_alterable(
  int n, int m, int op, void(*proc)(void)
) {
  if(cease_decode) return;
  main_eamode = aind; idef(n, m | 0x38, op | 0x10, proc);
  main_eamode = ainc; idef(n, m | 0x38, op | 0x18, proc);
  main_eamode = adec; idef(n, m | 0x38, op | 0x20, proc);
  main_eamode = adsp; idef(n, m | 0x38, op | 0x28, proc);
  main_eamode = axdp; idef(n, m | 0x38, op | 0x30, proc);
  main_eamode = absw; idef(n, m | 0x3F, op | 0x38, proc);
  main_eamode = absl; idef(n, m | 0x3F, op | 0x39, proc);
}

/* Batch idef for all control addressing modes */
static void eadef_control(
  int n, int m, int op, void(*proc)(void)
) {
  if(cease_decode) return;
  main_eamode = aind; idef(n, m | 0x38, op | 0x10, proc);
  main_eamode = adsp; idef(n, m | 0x38, op | 0x28, proc);
  main_eamode = axdp; idef(n, m | 0x38, op | 0x30, proc);
  main_eamode = absw; idef(n, m | 0x3F, op | 0x38, proc);
  main_eamode = absl; idef(n, m | 0x3F, op | 0x39, proc);
  main_eamode = pcdp; idef(n, m | 0x3F, op | 0x3A, proc);
  main_eamode = pcxd; idef(n, m | 0x3F, op | 0x3B, proc);
}

/* Batch idef for all control alterable addressing modes */
static void eadef_control_alterable(
  int n, int m, int op, void(*proc)(void)
) {
  if(cease_decode) return;
  main_eamode = aind; idef(n, m | 0x38, op | 0x10, proc);
  main_eamode = adsp; idef(n, m | 0x38, op | 0x28, proc);
  main_eamode = axdp; idef(n, m | 0x38, op | 0x30, proc);
  main_eamode = absw; idef(n, m | 0x3F, op | 0x38, proc);
  main_eamode = absl; idef(n, m | 0x3F, op | 0x39, proc);
}

/* Batch eadef for MOVE instructions */
static void defmove(int majorop, int n) {
  if(cease_decode) return;
  for(main_reg = 0; main_reg < 8; main_reg++) {
    word w = majorop | (main_reg << 9);
    main_destmode = dreg; eadef_all(n, 0xFFC0, 0x000 | w, i_move);
    main_destmode = aind; eadef_all(n, 0xFFC0, 0x080 | w, i_move);
    main_destmode = ainc; eadef_all(n, 0xFFC0, 0x0C0 | w, i_move);
    main_destmode = adec; eadef_all(n, 0xFFC0, 0x100 | w, i_move);
    main_destmode = adsp; eadef_all(n, 0xFFC0, 0x140 | w, i_move);
    main_destmode = axdp; eadef_all(n, 0xFFC0, 0x180 | w, i_move);
  }
  main_destmode = absw; eadef_all(n, 0xFFC0, 0x1C0 | majorop, i_move);
  main_destmode = absl; eadef_all(n, 0xFFC0, 0x3C0 | majorop, i_move);
}

/***************************************************************************/

static void decode0(int n) {
  cease_decode = 0;
  for(sizedef = 0; sizedef < 3; sizedef++) {
    main_size = 1 << sizedef;
    eadef_data_alterable(n, 0xFFC0, 0x0000 | (sizedef << 6), i_ori);
    eadef_data_alterable(n, 0xFFC0, 0x0200 | (sizedef << 6), i_andi);
    eadef_data_alterable(n, 0xFFC0, 0x0400 | (sizedef << 6), i_subi);
    eadef_data_alterable(n, 0xFFC0, 0x0600 | (sizedef << 6), i_addi);
    eadef_data_alterable(n, 0xFFC0, 0x0A00 | (sizedef << 6), i_eori);
    eadef_data_alterable(n, 0xFFC0, 0x0C00 | (sizedef << 6), i_cmpi);
    /* Not quite ready for prime time yet */
/*    if(cputype >= 68010) {
      eadef_memory_alterable(n, 0xFFC0, 0x0E00 | (sizedef << 6), i_moves);
    }*/
  }
  /*
  ** Bit operations
  ** BTST (main_cc 0) works with all data addressing modes; the others
  ** require data alterable addressing modes
  */
  for(main_cc = 0; main_cc < 4; main_cc++) {
    void (*eadef)(int n, int m, int op, void(*proc)(void)) =
      main_cc ? eadef_data_alterable : eadef_data;
    eadef(n, 0xFFC0, 0x0800 | (main_cc << 6), i_bitop_imm);
    for(main_reg = 0; main_reg < 8; main_reg++) {
      eadef(n, 0xFFC0,
        0x0100 | (main_cc << 6) | (main_reg << 9),
        i_bitop_reg
      );
    }
  }
  idef(n, 0xFFFF, 0x003C, i_ori_ccr );
  idef(n, 0xFFFF, 0x023C, i_andi_ccr);
  idef(n, 0xFFFF, 0x0A3C, i_eori_ccr);
  idef(n, 0xFFFF, 0x007C, i_ori_sr  );
  idef(n, 0xFFFF, 0x027C, i_andi_sr );
  idef(n, 0xFFFF, 0x0A7C, i_eori_sr );
  for(main_reg=0;main_reg<8;main_reg++)for(main_size=2;main_size<=4;main_size+=2){
    idef(n,0xFFF8,0x0108|((main_size&4)<<4)|(main_reg<<9),i_movep_mem2reg);
    idef(n,0xFFF8,0x0188|((main_size&4)<<4)|(main_reg<<9),i_movep_reg2mem);
  }
}

static void decode1(int n) {
  cease_decode = 0;
  main_size = 1;
  defmove(0x1000, n);
}

static void decode2(int n) {
  cease_decode = 0;
  main_size = 4;
  defmove(0x2000, n);
  for(main_reg = 0; main_reg < 8; main_reg++) {
    eadef_all(n, 0xFFC0, 0x2040 | (main_reg << 9), i_movea);
  }
}

static void decode3(int n) {
  cease_decode = 0;
  main_size = 2;
  defmove(0x3000, n);
  for(main_reg = 0; main_reg < 8; main_reg++) {
    eadef_all(n, 0xFFC0, 0x3040 | (main_reg << 9), i_movea);
  }
}

static void decode4(int n) {
  cease_decode = 0;
  eadef_data_alterable(n, 0xFFC0, 0x40C0, i_move_from_sr);
  if(cputype >= 68010) {
    eadef_data_alterable(n, 0xFFC0, 0x42C0, i_move_from_ccr);
  }
  eadef_data(n, 0xFFC0, 0x44C0, i_move_to_ccr);
  eadef_data(n, 0xFFC0, 0x46C0, i_move_to_sr );
  eadef_control(n, 0xFFC0, 0x4EC0, i_jmp);
  eadef_control(n, 0xFFC0, 0x4E80, i_jsr);
  for(main_reg=0;main_reg<8;main_reg++)eadef_control(n,0xFFC0,0x41C0|(main_reg<<9),i_lea);
  main_size=2;for(main_reg=0;main_reg<8;main_reg++)eadef_data(n,0xFFC0,0x4100|(main_reg<<9),i_chk);
  eadef_control(n,0xFFC0,0x4840,i_pea);
  for(sizedef = 0; sizedef < 3; sizedef++) {
    main_size = 1 << sizedef;
    eadef_data_alterable(n, 0xFFC0, 0x4200 | (sizedef << 6), i_clr);
    eadef_data_alterable(n, 0xFFC0, 0x4A00 | (sizedef << 6), i_tst);
  }
  idef(n, 0xFFFF, 0x4E70, i_reset);
  idef(n, 0xFFFF, 0x4E71, i_nop);
  idef(n, 0xFFFF, 0x4E72, i_stop);
  idef(n, 0xFFFF, 0x4E73, i_rte);
  idef(n, 0xFFFF, 0x4E75, i_rts);
  idef(n, 0xFFFF, 0x4E76, i_trapv);
  idef(n, 0xFFFF, 0x4E77, i_rtr);
  if(cputype >= 68010) {
    idef(n, 0xFFFF, 0x4E74, i_rtd);
    idef(n, 0xFFFF, 0x4E7A, i_movec_c_to_r);
    idef(n, 0xFFFF, 0x4E7B, i_movec_r_to_c);
  }
  main_dr=0;for(sizedef=0;sizedef<2;sizedef++){main_size=1<<(sizedef+1);eadef_control_alterable(n,0xFFC0,0x4880|(main_dr<<10)|(sizedef<<6),i_movem_control);}
  main_dr=1;for(sizedef=0;sizedef<2;sizedef++){main_size=1<<(sizedef+1);eadef_control          (n,0xFFC0,0x4880|(main_dr<<10)|(sizedef<<6),i_movem_control);}
  for(sizedef = 0; sizedef < 2; sizedef++) {
    main_size = 1 << (sizedef + 1);
    idef(n, 0xFFF8, 0x4C98 | (sizedef << 6), i_movem_postinc);
    idef(n, 0xFFF8, 0x48A0 | (sizedef << 6), i_movem_predec );
  }
  idef(n, 0xFFF8, 0x4E50, i_link);
  idef(n, 0xFFF8, 0x4E58, i_unlk);
  idef(n, 0xFFF0, 0x4E40, i_trap);
  idef(n, 0xFFF8, 0x4E60, i_move_to_usp);
  idef(n, 0xFFF8, 0x4E68, i_move_from_usp);
  idef(n, 0xFFF8, 0x4840, i_swap);
  idef(n, 0xFFF8, 0x4880, i_extbw);
  idef(n, 0xFFF8, 0x48C0, i_extwl);
  for(sizedef = 0; sizedef < 3; sizedef++) {
    main_size = 1 << sizedef;
    eadef_data_alterable(n, 0xFFC0, 0x4000 | (sizedef << 6),
      i_negx);
    eadef_data_alterable(n, 0xFFC0, 0x4400 | (sizedef << 6),
      i_neg);
    eadef_data_alterable(n, 0xFFC0, 0x4600 | (sizedef << 6),
      i_not);
  }
  eadef_data_alterable(n, 0xFFC0, 0x4800, i_nbcd);
  eadef_data_alterable(n, 0xFFC0, 0x4AC0, i_tas);
  if(cputype == 68010) idef(n, 0xFFF8, 0x4848, i_bkpt);
  idef(n, 0xFFFF, 0x4AFA, i_illegal);
  idef(n, 0xFFFF, 0x4AFB, i_illegal);
  idef(n, 0xFFFF, 0x4AFC, i_illegal);
}

static void decode5(int n) {
  cease_decode = 0;
  for(sizedef = 0; sizedef < 3; sizedef++) {
    main_size = 1 << sizedef;
    for(main_qv = 0; main_qv < 8; main_qv++) {
      word w = (sizedef << 6) | (main_qv << 9);
      eadef_alterable_nobyteaddress(n, 0xFFC0, 0x5000 | w,
        i_addq);
      eadef_alterable_nobyteaddress(n, 0xFFC0, 0x5100 | w,
        i_subq);
    }
  }
  for(main_cc = 0x2; main_cc <= 0xF; main_cc++) {
    idef(n, 0xFFF8, 0x50C8 | (main_cc << 8), i_dbcc);
  }
  idef(n, 0xFFF8, 0x51C8, i_dbra);
  main_size = 1;
  for(main_cc = 0x0; main_cc <= 0xF; main_cc++) {
    eadef_data_alterable(n, 0xFFC0, 0x50C0 | (main_cc << 8),
      i_scc);
  }
}

static void decode6(int n){
  cease_decode=0;
  idef(n,0x1FFFF,0x6000,i_bra_w);
  idef(n,0x1FFFF,0x6100,i_bsr_w);
  for(main_cc=0x2;main_cc<=0xF;main_cc++){
    idef(n,0x1FFFF,0x6000|(main_cc<<8),i_bcc_w);
  }
  idef(n,0x0FF00,0x6000,i_bra_b);
  idef(n,0x0FF00,0x6100,i_bsr_b);
  for(main_cc=0x2;main_cc<=0xF;main_cc++){
    idef(n,0x0FF00,0x6000|(main_cc<<8),i_bcc_b);
  }
}

static void decode7(int n){
  cease_decode=0;
  for(main_reg=0;main_reg<8;main_reg++)idef(n,0xFF00,0x7000|(main_reg<<9),i_moveq);
}

static void decode8(int n){
  cease_decode=0;
  for(sizedef=0;sizedef<3;sizedef++){main_size=1<<sizedef;for(main_reg=0;main_reg<8;main_reg++){eadef_data             (n,0xFFC0,0x8000|(main_reg<<9)|(sizedef<<6),i_or_dn );}}
  for(sizedef=0;sizedef<3;sizedef++){main_size=1<<sizedef;for(main_reg=0;main_reg<8;main_reg++){eadef_memory_alterable (n,0xFFC0,0x8100|(main_reg<<9)|(sizedef<<6),i_or_ea );}}
  for(main_cc=0;main_cc<2;main_cc++)for(main_reg=0;main_reg<8;main_reg++)eadef_data(n,0xFFC0,0x80C0|(main_reg<<9)|(main_cc<<8),i_div);
  main_size=1;
  for(main_reg=0;main_reg<8;main_reg++)idef(n,0xFFF8,0x8100|(main_reg<<9),i_sbcd_dreg);
  for(main_reg=0;main_reg<8;main_reg++)idef(n,0xFFF8,0x8108|(main_reg<<9),i_sbcd_adec);
}

static void decode9(int n){
  cease_decode=0;
  for(sizedef=0;sizedef<3;sizedef++){main_size=1<<sizedef;for(main_reg=0;main_reg<8;main_reg++){eadef_all_nobyteaddress(n,0xFFC0,0x9000|(main_reg<<9)|(sizedef<<6),i_sub_dn);}}
  for(sizedef=0;sizedef<3;sizedef++){main_size=1<<sizedef;for(main_reg=0;main_reg<8;main_reg++){eadef_memory_alterable (n,0xFFC0,0x9100|(main_reg<<9)|(sizedef<<6),i_sub_ea);}}
  main_size=2;for(main_reg=0;main_reg<8;main_reg++)eadef_all(n,0xFFC0,0x90C0|(main_reg<<9),i_suba);
  main_size=4;for(main_reg=0;main_reg<8;main_reg++)eadef_all(n,0xFFC0,0x91C0|(main_reg<<9),i_suba);
  for(sizedef=0;sizedef<3;sizedef++){main_size=1<<sizedef;for(main_reg=0;main_reg<8;main_reg++)idef(n,0xFFF8,0x9100|(main_reg<<9)|(sizedef<<6),i_subx_dreg);}
  for(sizedef=0;sizedef<3;sizedef++){main_size=1<<sizedef;for(main_reg=0;main_reg<8;main_reg++)idef(n,0xFFF8,0x9108|(main_reg<<9)|(sizedef<<6),i_subx_adec);}
}

static void decodeA(int n){
  cease_decode=0;
  idef(n,0xF000,0xA000,i_aline);
}

static void decodeB(int n){
  cease_decode=0;
  for(sizedef=0;sizedef<3;sizedef++){main_size=1<<sizedef;for(main_reg=0;main_reg<8;main_reg++){eadef_all_nobyteaddress(n,0xFFC0,0xB000|(main_reg<<9)|(sizedef<<6),i_cmp_dn);}}
  for(sizedef=0;sizedef<3;sizedef++){main_size=1<<sizedef;for(main_reg=0;main_reg<8;main_reg++){eadef_data_alterable   (n,0xFFC0,0xB100|(main_reg<<9)|(sizedef<<6),i_eor_ea);}}
  main_size=2;for(main_reg=0;main_reg<8;main_reg++)eadef_all(n,0xFFC0,0xB0C0|(main_reg<<9),i_cmpa);
  main_size=4;for(main_reg=0;main_reg<8;main_reg++)eadef_all(n,0xFFC0,0xB1C0|(main_reg<<9),i_cmpa);
  for(sizedef=0;sizedef<3;sizedef++){main_size=1<<sizedef;for(main_reg=0;main_reg<8;main_reg++)idef(n,0xFFF8,0xB108|(sizedef<<6)|(main_reg<<9),i_cmpm);}
}

static void decodeC(int n){
  cease_decode=0;
  for(sizedef=0;sizedef<3;sizedef++){main_size=1<<sizedef;for(main_reg=0;main_reg<8;main_reg++){eadef_data             (n,0xFFC0,0xC000|(main_reg<<9)|(sizedef<<6),i_and_dn);}}
  for(sizedef=0;sizedef<3;sizedef++){main_size=1<<sizedef;for(main_reg=0;main_reg<8;main_reg++){eadef_memory_alterable (n,0xFFC0,0xC100|(main_reg<<9)|(sizedef<<6),i_and_ea);}}
  for(main_cc=0;main_cc<2;main_cc++)for(main_reg=0;main_reg<8;main_reg++)eadef_data(n,0xFFC0,0xC0C0|(main_reg<<9)|(main_cc<<8),i_mul);
  main_dr=0 ;main_ir=0 ;for(main_reg=0;main_reg<8;main_reg++)idef(n,0xFFF8,0xC140|(main_reg<<9),i_exg);
  main_dr=32;main_ir=32;for(main_reg=0;main_reg<8;main_reg++)idef(n,0xFFF8,0xC148|(main_reg<<9),i_exg);
  main_dr=0 ;main_ir=32;for(main_reg=0;main_reg<8;main_reg++)idef(n,0xFFF8,0xC188|(main_reg<<9),i_exg);
  main_size=1;
  for(main_reg=0;main_reg<8;main_reg++)idef(n,0xFFF8,0xC100|(main_reg<<9),i_abcd_dreg);
  for(main_reg=0;main_reg<8;main_reg++)idef(n,0xFFF8,0xC108|(main_reg<<9),i_abcd_adec);
}

static void decodeD(int n){
  cease_decode=0;
  for(sizedef=0;sizedef<3;sizedef++){main_size=1<<sizedef;for(main_reg=0;main_reg<8;main_reg++){eadef_all_nobyteaddress(n,0xFFC0,0xD000|(main_reg<<9)|(sizedef<<6),i_add_dn);}}
  for(sizedef=0;sizedef<3;sizedef++){main_size=1<<sizedef;for(main_reg=0;main_reg<8;main_reg++){eadef_memory_alterable (n,0xFFC0,0xD100|(main_reg<<9)|(sizedef<<6),i_add_ea);}}
  main_size=2;for(main_reg=0;main_reg<8;main_reg++)eadef_all(n,0xFFC0,0xD0C0|(main_reg<<9),i_adda);
  main_size=4;for(main_reg=0;main_reg<8;main_reg++)eadef_all(n,0xFFC0,0xD1C0|(main_reg<<9),i_adda);
  for(sizedef=0;sizedef<3;sizedef++){main_size=1<<sizedef;for(main_reg=0;main_reg<8;main_reg++)idef(n,0xFFF8,0xD100|(main_reg<<9)|(sizedef<<6),i_addx_dreg);}
  for(sizedef=0;sizedef<3;sizedef++){main_size=1<<sizedef;for(main_reg=0;main_reg<8;main_reg++)idef(n,0xFFF8,0xD108|(main_reg<<9)|(sizedef<<6),i_addx_adec);}
}

static void decodeE(int n){
  cease_decode=0;
  for(main_reg=0;main_reg<8;main_reg++)
  for(main_dr=0;main_dr<2;main_dr++)
  for(main_ir=0;main_ir<2;main_ir++)
  for(sizedef=0;sizedef<3;sizedef++){
    main_size=1<<sizedef;
    idef(n,0xFFF8,0xE000|(main_reg<<9)|(main_dr<<8)|(sizedef<<6)|(main_ir<<5),i_asx_reg);
    idef(n,0xFFF8,0xE008|(main_reg<<9)|(main_dr<<8)|(sizedef<<6)|(main_ir<<5),i_lsx_reg);
    idef(n,0xFFF8,0xE010|(main_reg<<9)|(main_dr<<8)|(sizedef<<6)|(main_ir<<5),i_rxx_reg);
    idef(n,0xFFF8,0xE018|(main_reg<<9)|(main_dr<<8)|(sizedef<<6)|(main_ir<<5),i_rox_reg);
  }
  for(main_dr=0;main_dr<2;main_dr++){
    eadef_memory_alterable(n,0xFFC0,0xE0C0|(main_dr<<8),i_asx_mem);
    eadef_memory_alterable(n,0xFFC0,0xE2C0|(main_dr<<8),i_lsx_mem);
    eadef_memory_alterable(n,0xFFC0,0xE4C0|(main_dr<<8),i_rxx_mem);
    eadef_memory_alterable(n,0xFFC0,0xE6C0|(main_dr<<8),i_rox_mem);
  }
}

static void decodeF(int n) {
  cease_decode = 0;
  idef(n, 0xF000, 0xF000, i_fline);
}

static void (*(decodetable[16]))(int n) =
{decode0, decode1, decode2, decode3, decode4, decode5, decode6, decode7,
 decode8, decode9, decodeA, decodeB, decodeC, decodeD, decodeE, decodeF};

/***************************************************************************/

/* Build a jump table entry (including loop info for 68010) */
static void tableentry(int last, int rl) {
  if(last == -1){
    emit("dd r_illegal-top");
  } else {
    emit("dd %c%03X-top",
      ((last >> 12) & 0xF) + 'K', last & 0xFFF
    );
  }
  if(rl > 1) emit("+%u", ((dword)(rl - 1)) << 24);
  emit("\n");
  if(cputype == 68010) emit("db %d\n", loopinfo[last]);
}

/* Return the next parameter (or NULL if there isn't one */
static char *getparameter(int *ip, int argc, char **argv) {
  int i;
  (*ip)++;
  i = (*ip);
  if(i >= argc) {
    fprintf(stderr, "Invalid use of %s option\n", argv[i - 1]);
    return NULL;
  }
  return argv[i];
}

int main(int argc, char **argv) {
  int i, j, last, rl, bank;
  char *codefilename = NULL;
  char default_sourcename[10];

  fprintf(stderr, "STARSCREAM version " VERSION "\n");

  /* Read options from the command line */
  for(i = 1; i < argc; i++) {
    char *a = argv[i];
    if(*a == '-') {
      a++;
             if(!strcmp("fastcall"   , a)) { use_stack = 0;
      } else if(!strcmp("cdecl"      , a)) { use_stack = 1;
      } else if(!strcmp("nohog"      , a)) { hog = 0;
      } else if(!strcmp("hog"        , a)) { hog = 1;
      } else if(!strcmp("addressbits", a)) {
        int n;
        char *s = getparameter(&i, argc, argv);
        if(!s) return 1;
        n = atol(s);
        if(n < 1 || n > 32) {
          fprintf(stderr,
            "Invalid number of address "
            "bits: \"%s\"\n", argv[i]
          );
          return 1;
        }
        addressbits = n;
      } else if(!strcmp("cputype"    , a)) {
        int n;
        char *s = getparameter(&i, argc, argv);
        if(!s) return 1;
        n = atol(s);
        switch(n) {
        case 68000:
        case 68010:
        case 68020:
          cputype = n;
          break;
        default:
          fprintf(stderr,
            "Invalid CPU type: \"%s\"\n",
            argv[i]
          );
          return 1;
        }
      } else if(!strcmp("name"       , a)) {
        sourcename = getparameter(&i, argc, argv);
        if(!sourcename) return 1;
      } else {
        fprintf(stderr,
          "\nUnrecognized option: \"%s\"\n",
          argv[i]
        );
        return 1;
      }
    } else {
      if(codefilename) {
        fprintf(stderr,
          "\n\"%s\": only one output filename "
          "is allowed\n",
          argv[i]
        );
        return 1;
      }
      codefilename = argv[i];
    }
  }

  if(!codefilename) {
    fprintf(stderr, "usage: %s outputfile [options]\n", argv[0]);
    fprintf(stderr, "see STARDOC.TXT for details\n");
    return 1;
  }

  /* Set default options where applicable */
  if(use_stack   < 0) use_stack = 1;
  if(hog         < 0) hog       = 0;
  if(cputype     < 0) cputype   = 68000;
  if(addressbits < 0) {
    if(cputype <= 68010) addressbits = 24;
    else                 addressbits = 32;
  }
  if(!sourcename) {
    sprintf(default_sourcename, "s%d", cputype);
    sourcename = default_sourcename;
  }

  /* Prepare to generate the code file */
  linenum = 0;
  fflush(stdout);
  fflush(stderr);
  codefile = fopen(codefilename, "w");
  if(!codefile) {
    perror(codefilename);
    return 1;
  }

  fprintf(stderr, "Generating \"%s\" with the following options:\n",
    codefilename
  );
  optiondump(stderr, " *  ");
  prefixes();
  for(i = 0; i < 0x10000; i++) rproc[i] = -1;
  /* Clear loop timings for 68010 */
  if(cputype == 68010) {
    for(i = 0; i < 0x10000; i++) loopinfo[i] = 0xDB;
  }

  /*
  ** Decode instructions
  ** (this is where the vast majority of the code is emitted)
  */
  fprintf(stderr, "Decoding instructions: ");
  for(bank = 0; bank <= 0xF; bank++) {
    int bankend = (bank + 1) << 12;
    void (*decoderoutine)(int n) = decodetable[bank];
    fprintf(stderr, "%X", bank);
    fflush(stderr);
    for(i = bank << 12; i < bankend; i++) decoderoutine(i);
  }
  fprintf(stderr, " done\n");

  /*
  ** Build the main jump table (all CPUs) / loop info table (68010)
  */
  fprintf(stderr, "Building table: ");
  emit("section .bss\n");
  emit("bits 32\n");
  align(4);
  emit("_jmptbl resb 262144\n");
  if(cputype == 68010) emit("_looptbl resb 65536\n");
  emit("section .data\n");
  emit("bits 32\n");
  align(4);
  emit("_jmptblcomp:\n");
  last = -2;
  rl = 0;
  for(i = 0; i < 0x10000; i++) {
    j = rproc[i];
    if(j == last){
      if(rl == 256) {
        tableentry(last, rl);
        rl = 1;
      } else {
        rl++;
      }
    } else {
      if(rl) tableentry(last, rl);
      rl = 1;
      last = j;
    }
  }
  tableentry(last, rl);
  align(4);

  /* Finish up */
  suffixes();
  fprintf(stderr, "done\n");
  fprintf(stderr, "routine_counter = %d\n", routine_counter);
  fclose(codefile);
  return 0;
}
